andorid开发也做了3年有余了,也面试很多加企业,借此机会分享一下,我们中遇到过的问题以及解决方案吧,希望能够对正在找工作的andoird程序员有一定的帮助。
特别献上整理过的50道面试题目
1.listView的优化方式
重用convertView |
viewHolder |
static class viewHolder |
在列表里面有图片的情况下,监听滑动不加载图片 |
多个不同布局,可以创建不同的viewHolder和convertView进行重用 |
从sqlite拉取数据源显示 |
从xml使用pull解析拉取数据源显示 |
从网络上拉取数据源显示 |
进程间通信主要包括管道, 系统IPC(Inter-Process Communication,进程间通信)(包括消息队列,信号,共享存储), 套接字(SOCKET). 目的: l 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。 l 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。 l 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 l 资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。 l 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。 进程通过与内核及其它进程之间的互相通信来协调它们的行为。Linux支持多种进程间通信(IPC)机制,信号和管道是其中的两种。除此之外,Linux还支持System V 的IPC机制(用首次出现的Unix版本命名)。 |
Android中的Parcel机制 实现了Bundle传递对象 使用Bundle传递对象,首先要将其序列化,但是,在Android中要使用这种传递对象的方式需要用到Android Parcel机制,即,Android实现的轻量级的高效的对象序列化和反序列化机制。 JAVA中的Serialize机制,译成串行化、序列化……,其作用是能将数据对象存入字节流当中,在需要时重新生成对象。主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。 Android中的新的序列化机制 在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。在这样的环境下, Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列化机制。 Android中序列化有以下几个特征: 1. 整个读写全是在内存中进行,所以效率比JAVA序列化中使用外部存储器会高很多; 2. 读写时是4字节对齐的 3. 如果预分配的空间不够时,会一次多分配50%; 4. 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。 代码: activity代码: Intent mIntent =newIntent(this,ParcelableDemo.class); Bundle mBundle =newBundle(); mBundle.putParcelable(PAR_KEY, mPolice); mIntent.putExtras(mBundle); 实体类:
|
(1) Eclipse中新建android工程 工程名 JNItest Package名com.ura.test Activity名 JNItest 应用程序名 JNItest (2) 编辑main.xml android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > android:id="@+id/JNITest" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/JNITest" /> (3)编辑java文件 package com.ura.test; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class JNITest extends Activity { /** Called when the activity is first created. */ static { System.loadLibrary("JNITest"); } public native String GetTest(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String str =GetTest(); TextView JNITest = (TextView)findViewById(R.id.JNITest); JNITest.setText(str); } } (4)生成head文件 编译上面工程声称class文件,然后用javah工具生成c/c++ 头文件 javah -classpath bin -d jni com.ura.test.JNItest 生成的头文件如下 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_ura_test_JNITest */ #ifndef _Included_com_ura_test_JNITest #define _Included_com_ura_test_JNITest #ifdef __cplusplus extern "C" { #endif /* * Class: com_ura_test_JNITest * Method: GetTest * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_ura_test_JNITest_GetTest (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif (5)编写c/c++文件如下 include "com_ura_test_JNITest.h" #define LOG_TAG "JNITest" #undef LOG #include JNIEXPORT jstring JNICALL Java_com_ura_test_JNITest_GetTest (JNIEnv * env, jobject obj) { return (*env)->NewStringUTF(env, (char *)"JNITest Native String"); LOGD("Hello LIB!\n"); } (6)编写android.mk文件 LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ com_ura_test_JNITest.c LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) LOCAL_SHARED_LIBRARIES := libutils LOCAL_PRELINK_MODULE := false LOCAL_MODULE := libJNITest include $(BUILD_SHARED_LIBRARY) (7)编译生成动态库 新建文件夹 ~/mydroid/external/libJNITest 把上面编写好的头文件,c/c++源文件,make文件拷贝进上面目录中 * 需要注意的是把PRELINK_MOUDULE设置成false 否则需要重新做成img文件再烧入。 在 ubuntu中执行 cd cd mydroid/build/ envsetup.sh cd ~/mydroid cd external/libJNITest/ mm 编译成功的后会在下面目录中生成libJNITest.so文件 ~mydroid/out/target/product/generic/system/lib/ (8)在模拟器中执行程序 首先要把动态库拷进/system/lib中。 启动模拟器 adb shell adb remount adb push libJNITest.so /system/lib 确认拷贝成功 cd /system/lib ls 然后不要关闭模拟器(关掉再开动态库就没了,因为模拟器rom是只读) 执行java程序JNITest 会看到屏幕上打印出 JNITest Native String |
四大组件之一,一般的,一个用户交互界面对应一个activity setContentView() ,// 要显示的布局 button.setOnclickLinstener{ } , activity 是Context的子类,同时实现了window.callback和keyevent.callback, 可以处理与窗体用户交互的事件. 里面不能进行耗时操作 我开发常用的的有ListActivity , PreferenceActivity ,TabAcitivty等… 如果界面有共同的特点或者功能的时候,还会自己定义一个BaseActivity. |
Activity生命周期 1 完整生命周期 onCreate() --> onStart() --> onResume() 可以在手机上看见activity ---> onPause() --> onStop() 看不见了 ---> onDestory() 销毁了 2 前台生命周期 onstart() ---> onStop()之间进行切换 onCreate() --> onStart() --> onResume() 现在有一个activity完全覆盖 onPause() ----> onStop() 如果上面的activity关闭 onRestart() ---> onStart() --> onResume() 3 可视生命周期 onResume() ---> onPause()之间进行切换 onCreate() --> onStart() --> onResume() 现在有一个activity没有完全覆盖 onPause() 如果上面的activity关闭 onResume() |
这个生命周期跟清单文件里的配置有关系 1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期 默认首先销毁当前activity,然后重新加载 2、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法 游戏开发中, 屏幕的朝向都是写死的. |
可以自定义一个activity的样式,详细见手机卫士的程序详细信息 android:theme="@style/FloatActivity" E:\day9\mobilesafe\res\values\style |
|
除了在栈顶的activity,其他的activity都有可能在内存不足的时候被系统回收,一个activity越处于栈底,被回收的可能性越大. protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putLong("id", 1234567890); } public void onCreate(Bundle savedInstanceState) { //判断savedInstanceState是不是空. //如果不为空就取出来 super.onCreate(savedInstanceState); } |
退出activity 直接调用 finish () 方法 . //用户点击back键 就是退出一个activity 退出activity 会执行 onDestroy()方法 . 1、抛异常强制退出: 该方法通过抛异常,使程序Force Close。 验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。 //安全结束进程 android.os.Process.killProcess(android.os.Process.myPid()); 2、记录打开的Activity: 每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。 List lists = new ArrayList lists.add(activity); for(Activity activity: lists) { activity.finish(); } 3、发送特定广播: 在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。 //给某个activity 注册接受接受广播的意图 registerReceiver(receiver, filter) //如果过接受到的是 关闭activity的广播 就调用finish()方法 把当前的activity finish()掉 4、递归退出 在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。 上面是网上的一些做法. 其实 可以通过 intent的flag 来实现.. intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一个新的activity,然后在新的activity的oncreate方法里面 finish掉. |
基本数据类型可以通过. Intent 传递数据 在A activity中 Intent intent = new Intent(); intent.putExtra(name, value) Bundle bundle = new Bundle(); bundle.putBoolean(key,value); intent.putExtras(bundle); extras.putDouble(key, value) // 通过intent putExtra 方法 基本数据类型 都传递 Intent i = getIntent(); i.getExtras(); intent.getStringExtra("key","value"); intent.getBooleanExtra("key","value") Bundle bundle = new Bundle(); bumdle.putShort(key, value); intent.putExtras(bumdle); intent.putExtras(bundle) -------------- Application 全局里面存放 对象 ,自己去实现自己的application的这个类, 基础系统的application , 每个activity都可以取到 ----------------- 让对象实现 implements Serializable 接口把对象存放到文件上. 让类实现Serializable 接口,然后可以通过ObjectOutputStream //对象输出流 File file = new File("c:\1.obj"); FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos); Student stu = new Student(); oos.writeObject(stu); //从文件中把对象读出来 ObjectInputStream ois = new ObjectInputStream(arg0); Student stu1 = (Student) ois.readObject(); 文件/网络 intent.setData(Uri) Uri.fromFile(); //大图片的传递 |
把上面的几点用自己的心得写出来 |
在Service的生命周期中,被回调的方法比Activity少一些,只有onCreate, onStart, onDestroy, onBind和onUnbind。 通常有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。 1 通过startService Service会经历 onCreate 到onStart,然后处于运行状态,stopService的时候调用onDestroy方法。 如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。 2 通过bindService Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。 所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbind->onDestroyed方法。 需要注意的是如果这几个方法交织在一起的话,会出现什么情况呢? 一个原则是Service的onCreate的方法只会被调用一次,就是你无论多少次的startService又bindService,Service只被创建一次。 如果先是bind了,那么start的时候就直接运行Service的onStart方法, 如果先是start,那么bind的时候就直接运行onBind方法。 如果service运行期间调用了bindService,这时候再调用stopService的话,service是不会调用onDestroy方法的,service就stop不掉了,只能调用UnbindService, service就会被销毁 如果一个service通过startService 被start之后,多次调用startService 的话,service会多次调用onStart方法。多次调用stopService的话,service只会调用一次onDestroyed方法。 如果一个service通过bindService被start之后,多次调用bindService的话,service只会调用一次onBind方法。 多次调用unbindService的话会抛出异常。 |
默认情况,如果没有显示的指定service所运行的进程, Service和activity是运行在当前app所在进程的main thread(UI主线程)里面 service里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 ) 在子线程中执行 new Thread(){}.start(); 特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让service在另外的进程中执行 |
在activity的onCreate()方法里面 startService(); |
startService() 一旦被创建 调用着无关 没法使用service里面的方法 bindService () 把service 与调用者绑定 ,如果调用者被销毁, service会销毁 bindService() 我们可以使用service 里面的方法 bindService(). 让activity能够访问到 service里面的方法 构建一个intent对象, Intent service = new Intent(this,MyService.class); 通过bindService的方法去启动一个服务, bindService(intent, new MyConn(), BIND_AUTO_CREATE); ServiceConnection 对象(重写onServiceConnected和OnServiceDisconnected方法) 和BIND_AUTO_CREATE. private class myconn implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub //可以通过IBinder的对象 去使用service里面的方法 } public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } } |
这个问题问的很山寨.默认不做任何处理,B里面的音乐都能播放. 遇到问题, 可以随机应变,灵活发挥,多考虑些细节,比如说这个题就可以这样说,说说你对startActivityForResult的理解() B的结束的时候 setResult() A会调用到onActivityResult() 就会获取到resultCode A开启B的时候,用startActivityForResult()方法, B返回的时候把播放的状态信息返回给A ,A继续播放音乐. seekTo(resultCode) |
普通的service ,默认运行在ui main 主线程 Sdk给我们提供的方便的,带有异步处理的service类, 可以在OnHandleIntent() 处理耗时的操作 |
后台操作,耗时操作的时候 拥有service的进程具有较高的优先级 官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。 1. 如果service正在调用onCreate, onStartCommand或者onDestory方法,那么用于当前service的进程相当于前台进程以避免被killed。 2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed. 3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。 4. 如果service可以使用startForeg round(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。 1.Service的特点可以让他在后台一直运行,可以在service里面创建线程去完成耗时的操作. new Thread(){ TimerTask // 循环的执行一个定时的任务 }.start(); 2.Broadcast receiver捕获到一个事件之后,可以起一个service来完成一个耗时的操作. ANR new Service() 3.远程的service如果被启动起来,可以被多次bind, 但不会重新create. 索爱手机X10i的人脸识别的service可以被图库使用,可以被摄像机,照相机等程序使用. 画廊 摄像机 照相机 bindService() Ibinder的对象, 访问service |
Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,int,int)方法,然后在onStartCommand方法中做一些处理。 从Android官方文档中,我们知道onStartCommand有4种int返回值,首先简单地讲讲int返回值的作用。 一、onStartCommand有4种返回值: START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。 START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。 START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。 START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。 二、创建不被杀死的service 1.在service中重写下面的方法,这个方法有三个返回值, START_STICKY(或START_STICKY_COMPATIBILITY)是service被kill掉后自动重写创建 @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY_COMPATIBILITY; //return super.onStartCommand(intent, flags, startId); } 或 @Override public int onStartCommand(Intent intent, int flags, int startId) { flags = START_STICKY; return super.onStartCommand(intent, flags, startId); // return START_REDELIVER_INTENT; } @Override public void onStart(Intent intent, int startId) { // 再次动态注册广播 IntentFilter localIntentFilter = new IntentFilter("android.intent.action.USER_PRESENT"); localIntentFilter.setPriority(Integer.MAX_VALUE);// 整形最大值 myReceiver searchReceiver = new myReceiver(); registerReceiver(searchReceiver, localIntentFilter); super.onStart(intent, startId); } 2.在Service的onDestroy()中重启Service. public void onDestroy() { Intent localIntent = new Intent(); localIntent.setClass(this, MyService.class); // 销毁时重新启动Service this.startService(localIntent); } 3.创建一个广播 public class myReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { context.startService(new Intent(context, Google.class)); } } 4.AndroidManifest.xml中注册广播myReceiver及MyService服务 注:解锁,启动,切换场景激活广播需加权限,如启动完成,及手机机状态等。 亲测ZTE U795手机Android 4.0.4版本adb push到system\app下android:persistent="true" 变成核心程序,在360杀掉进程的时候,myReceiver照样有效,保证service重生。呃 KILL问题: 1. settings 中stop service onDestroy方法中,调用startService进行Service的重启。 2.settings中force stop 应用 捕捉系统进行广播(action为android.intent.action.PACKAGE_RESTARTED) 3. 借助第三方应用kill掉running task 提升service的优先级,程序签名,或adb push到system\app下等 相较于/data/app下的应用,放在/system/app下的应用享受更多的特权,比如若在其Manifest.xml文件中设置persistent属性为true,则可使其免受out-of-memory killer的影响。如应用程序'Phone'的AndroidManifest.xml文件: android:persistent="true" android:label="@string/dialerIconLabel" android:icon="@drawable/ic_launcher_phone"> ... 设置后app提升为系统核心级别 |
下面是Android Doc中关于BroadcastReceiver的概述:①广播接收器是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统代码的──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。 ②应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。 ③广播接收器没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。 有很多广播接收者 ,系统已经实现了. 广播分两种 有序广播 无序广播 指定接收者的有序广播 . sendOrderedBroadcast(intent,receiverPermission,resultReceiver,scheduler,initialCode,initialData,initialExtras) 接受者一定会获取到 广播的事件 sendStickyBroadcast(intent) //阴魂不散 广播接受者在onReceive 方法获取到广播的事件 Wifi设置 等待wifi状态更新完毕 是不可以被拦截掉的 abortBroadcast(); 代码配置优先级比xml配置优先级的级别高,因为代码运行在内存中,而清单在系统中 手机卫士中自定义一个broadcast receiver 来获取短信到来的广播, 根据黑名单来判断是否拦截该短信. 画画板生成图片后,发送一个sd挂载的通知,通知系统的gallery去获取到新的图片. Intent intent = newIntent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse("file://"+Environment.getExternalStorageDirectory())); sendBroadcast(intent); |
用于接收系统的广播通知, 系统会有很多sd卡挂载,手机重启,广播通知,低电量,来电,来短信等…. |
设置广播接收者的优先级,设置广播接受者的action名字 等… 详细见工程代码. 可以通过代码 registerReceiver(receiver,filter) |
|
需要访问别人的数据的时候 |
1.先是提供的数据类型等数据的类。package org.juetion.cp; import android.net.Uri; import android.provider.BaseColumns; /** * 提供的数据类型等数据。 * Created by juetionke on 13-12-21. */ public class MyProviderMetaData { public static final String AUTHORIY = "org.juetion.cp.MyContentProvider"; /** * 数据库名称 */ public static final String DATABASE_NAME = "MyProvider.db"; /** * 数据库版本 */ public static final int DATABASE_VERSION = 1; /** * 表名 */ public static final String USERS_TABLE_NAME = "users"; /** * 继承了BaseColumns,所以已经有了_ID */ public static final class UserTableMetaData implements BaseColumns { /** * 表名 */ public static final String TABLE_NAME = "users"; /** * 访问该ContentProvider的URI */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORIY + "/users"); /** * 该ContentProvider所返回的数据类型定义 */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/org.juetion.user"; public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/org.juetion.user"; /** * 列名 */ public static final String USER_NAME = "name"; public static final String USER_AGE = "age"; /** * 默认的排序方法 */ public static final String DEFAULT_SORT_ORDER = "_id desc"; } } 2,继承ContentProvider的类: package org.juetion.cp; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import org.juetion.sqlite3.DatabaseHelper; import java.util.HashMap; /** * Created by juetionke on 13-12-21. */ public class MyContentProvider extends ContentProvider { /** * 定义规则 */ public static final UriMatcher uriMatcher; public static final int USERS_COLLECTION = 1;//用于标记 public static final int USERS_SINGLE = 2;//用于标记 private DatabaseHelper databaseHelper;//这里的数据共享是共享Sqlite里的数据,当然,可以试用其他,如文本数据共享。 static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);//试用一个没有规则的Uri。然后下面自己匹配。 uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users",USERS_COLLECTION);//自己定义的规则,有点像路由器,是uri匹配的方案。 uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users/#",USERS_SINGLE);//同上。 } /** * 为列定义别名 */ public static HashMap static { usersMap = new HashMap usersMap.put(MyProviderMetaData.UserTableMetaData._ID, MyProviderMetaData.UserTableMetaData._ID); usersMap.put(MyProviderMetaData.UserTableMetaData.USER_NAME, MyProviderMetaData.UserTableMetaData.USER_NAME); usersMap.put(MyProviderMetaData.UserTableMetaData.USER_AGE, MyProviderMetaData.UserTableMetaData.USER_AGE); } @Override public boolean onCreate() { Log.i("juetion","onCreate"); databaseHelper = new DatabaseHelper(getContext(), MyProviderMetaData.DATABASE_NAME);//这里的实现,常见前篇关于Sqlite的文章。 return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.i("juetion","query"); SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();//写入查询条件,有点像Hibernate。 switch (uriMatcher.match(uri)) {//判断查询的是单个数据还是多个数据。 case USERS_SINGLE: sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME);//需要查询的表 sqLiteQueryBuilder.setProjectionMap(usersMap);//列的别名定义 sqLiteQueryBuilder.appendWhere(MyProviderMetaData.UserTableMetaData._ID + "=" + uri.getPathSegments().get(1)); //查询条件,uri.getPathSegments().get(1),getPathSegments是将内容根据/划分成list。 break; case USERS_COLLECTION: sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME); sqLiteQueryBuilder.setProjectionMap(usersMap); break; } String orderBy;//判断sortOrder是否为空,加入默认。 if (TextUtils.isEmpty(sortOrder)) { orderBy = MyProviderMetaData.UserTableMetaData.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase(); Cursor cursor = sqLiteQueryBuilder.query(sqLiteDatabase, projection, selection, selectionArgs, null, null, sortOrder);//可以使用下面的方法,不过此时sqLiteDatabase将会没有用。 //Cursor cursor = sqLiteDatabase.query(MyProviderMetaData.UserTableMetaData.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy); cursor.setNotificationUri(getContext().getContentResolver(),uri); return cursor; } /** * 根据传入的URI,返回URI说表示的数据类型 * @param uri * @return */ @Override public String getType(Uri uri) { Log.i("juetion","getType"); switch (uriMatcher.match(uri)) {//匹配uri的规则 case USERS_COLLECTION: return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE; case USERS_SINGLE: return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM; default: throw new IllegalArgumentException("Unknown URI" + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { Log.i("juetion","insert"); SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase(); long rowId = sqLiteDatabase.insert(MyProviderMetaData.UserTableMetaData.TABLE_NAME, null, values); if (rowId > 0) { Uri insertUserUri = ContentUris.withAppendedId(MyProviderMetaData.UserTableMetaData.CONTENT_URI, rowId);//简单来说就是字符串拼凑一下。只不过是uri专用的。 //通知监听器 getContext().getContentResolver().notifyChange(insertUserUri,null); return insertUserUri; }else throw new IllegalArgumentException("Failed to insert row into" + uri); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Log.i("juetion","delete"); return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.i("juetion","update"); return 0; } } 还有重要的一点,再第二个APP的AndroidManifest.xml里面需要添加 android:authorities="org.juetion.cp.MyContentProvider" android:name="org.juetion.cp.MyContentProvider”/> 以下代码是在第一个APP里面的。 关于使用。只需要将uri的string提供给第一个APP。 例如在第一个APP的Activity调用数据插入: ContentValues contentValues = new ContentValues(); contentValues.put("name","zhangsan"); contentValues.put("age",19); Uri uri = getContentResolver().insert(Uri.parse("content://org.juetion.cp.MyContentProvider/users"),contentValues); Log.i("juetion", "insert uri-->" + uri.toString()); 例如在第一个APP的Activity调用数据的查询: Cursor cursor = getContentResolver().query(Uri.parse("content://org.juetion.cp.MyContentProvider/users"), new String[]{"name", "age"}, null, null, null); while (cursor.moveToNext()) { Log.i("juetion", cursor.getString(cursor.getColumnIndex("name"))); } |
ContentProvider 可以屏蔽数据操作的细节 文件 xml MyContentProvider 可以在不同应用程序之间共享数据 sharedpreference db 把自己的数据通过uri的形式共享出去 android 系统下 不同程序 数据默认是不能共享访问 需要去实现一个类去继承ContentProvider public class PersonContentProvider extends ContentProvider{ public boolean onCreate(){ //.. } query(Uri, String[], String, String[], String) insert(Uri, ContentValues) update(Uri, ContentValues, String, String[]) delete(Uri, String, String[]) 联系人的信息 sms的内容content://sms/ } |
android安全学习 签名作用 1.sharedUserId 一样并且签名一次 可以实现数据共享 2.升级应用 权限:细粒度的特权管理 权限与操作关联 应用需要显式申请权限 用户对权限可知(不可控) 对特权权限单独控制 四大组件 exported = true 等于public exported = false 等于private 默认组件private 如果该组件设置了intent-filter默认是public 如果同时希望是private,就需要主动设置expoted=false Securing Activities 可知指定权限才能启动activity service同上 BoradcastReceiver可以设置接发的权限 ContentPrivider 可设置读写Permission |
本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信。 一、Android当中的多线程 在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行。默认的情况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程我们称之为Main线程。当我们通过某个组件来启动另一个组件的时候,这个时候默认都是在同一个线程当中完成的。当然,我们可以自己来管理我们的Android应用的线程,我们可以根据我们自己的需要来给应用程序创建额外的线程。 二、Main Thread 和 Worker Thread 在Android当中,通常将线程分为两种,一种叫做Main Thread,除了Main Thread之外的线程都可称为Worker Thread。 当一个应用程序运行的时候,Android操作系统就会给该应用程序启动一个线程,这个线程就是我们的Main Thread,这个线程非常的重要,它主要用来加载我们的UI界面,完成系统和我们用户之间的交互,并将交互后的结果又展示给我们用户,所以Main Thread又被称为UI Thread。 Android系统默认不会给我们的应用程序组件创建一个额外的线程,所有的这些组件默认都是在同一个线程中运行。然而,某些时候当我们的应用程序需要完成一个耗时的操作的时候,例如访问网络或者是对数据库进行查询时,此时我们的UI Thread就会被阻塞。例如,当我们点击一个Button,然后希望其从网络中获取一些数据,如果此操作在UI Thread当中完成的话,当我们点击Button的时候,UI线程就会处于阻塞的状态,此时,我们的系统不会调度任何其它的事件,更糟糕的是,当我们的整个现场如果阻塞时间超过5秒钟(官方是这样说的),这个时候就会出现 ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来说,出现ANR的现象是绝对不能被允许的。 另外,由于我们的Android UI控件是线程不安全的,所以我们不能在UI Thread之外的线程当中对我们的UI控件进行操作。因此在Android的多线程编程当中,我们有两条非常重要的原则必须要遵守: 绝对不能在UI Thread当中进行耗时的操作,不能阻塞我们的UI Thread 不能在UI Thread之外的线程当中操纵我们的UI元素 三、如何处理UI Thread 和 Worker Thread之间的通信 既然在Android当中有两条重要的原则要遵守,那么我们可能就有疑问了?我们既不能在主线程当中处理耗时的操作,又不能在工作线程中来访问我们的UI控件,那么我们比如从网络中要下载一张图片,又怎么能将其更新到UI控件上呢?这就关系到了我们的主线程和工作线程之间的通信问题了。在Android当中,提供了两种方式来解决线程直接的通信问题,一种是通过Handler的机制(这种方式在后面的随笔中将详细介绍),还有一种就是今天要详细讲解的 AsyncTask 机制。 四、AsyncTask AsyncTask:异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成一些操作。AsyncTask允许我们的执行一个异步的任务在后台。我们可以将耗时的操作放在异步任务当中来执行,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。通过AsyncTask我们可以轻松的解决多线程之间的通信问题。 怎么来理解AsyncTask呢?通俗一点来说,AsyncTask就相当于Android给我们提供了一个多线程编程的一个框架,其介于Thread和Handler之间,我们如果要定义一个AsyncTask,就需要定义一个类来继承AsyncTask这个抽象类,并实现其唯一的一个 doInBackgroud 抽象方法。要掌握AsyncTask,我们就必须要一个概念,总结起来就是: 3个泛型,4个步骤。 3个泛型指的是什么呢?我们来看看AsyncTask这个抽象类的定义,当我们定义一个类来继承AsyncTask这个类的时候,我们需要为其指定3个泛型参数: AsyncTask Params: 这个泛型指定的是我们传递给异步任务执行时的参数的类型 Progress: 这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型 Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型 我们在定义一个类继承AsyncTask类的时候,必须要指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void,例如: AsyncTask 4个步骤:当我们执行一个异步任务的时候,其需要按照下面的4个步骤分别执行 onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出要给ProgressDialog doInBackground(Params... params): 在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,所以这个方法是在worker thread当中执行的,这个方法执行完之后就可以将我们的执行结果发送给我们的最后一个 onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作 onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,有时候需要将执行的进度返回给我们的UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将我们的进度时时刻刻传递给 onProgressUpdate 方法来更新 onPostExecute(Result... result): 当我们的异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上 为什么我们的AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??原因是,我们如果要做一个异步任务,我们必须要为其开辟一个新的Thread,让其完成一些操作,而在完成这个异步任务时,我可能并不需要弹出要给ProgressDialog,我并不需要随时更新我的ProgressDialog的进度条,我也并不需要将结果更新给我们的UI界面,所以除了 doInBackground 方法之外的三个方法,都不是必须有的,因此我们必须要实现的方法是 doInBackground 方法。 五、通过AsyncTask来从网络上下载一张图片 下面我们就通过两个代码示例,来看看如何通过AsyncTask来从网络上下载一张图片,并更新到我们的ImageView控件上。 ①下载图片时,弹出一个ProgressDialog,但是不显示实时进度 我们来看看布局文件: <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"]] > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="200dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:scaleType="fitCenter"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/imageView" android:layout_centerHorizontal="true" android:layout_marginTop="41dp" android:text="从网络上下载一张图片"/> RelativeLayout]] > 就是很简单的一个ImageView控件和一个Button控件,当点击Button控件时,弹出一个ProgressDialog,然后开启一个异步任务,从网络中下载一张图片,并更新到我们的ImageView上。这里还要注意一点,如果我们要使用手机访问网络,必须还要给其授权才行,在后续的学习当中,将会详细讲解Android当中的授权的知识。我们来看看 AndroidManifest.xml文件: xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android" package="com.xiaoluo.android_asynctast" android:versionCode="1" android:versionName="1.0"]] > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/> <uses-permissionandroid:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"]] > <activity android:name="com.xiaoluo.android_asynctast.MainActivity" android:label="@string/app_name"]] > <intent-filter]] > <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> intent-filter]] > activity]] > application]] > manifest]] > 接下来我们来看看我们的Activity代码: publicclass MainActivityextends Activity { private Button button; private ImageView imageView; private ProgressDialog progressDialog; privatefinal String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg"; // private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg"; @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button); imageView = (ImageView)findViewById(R.id.imageView); // 弹出要给ProgressDialog progressDialog =new ProgressDialog(MainActivity.this); progressDialog.setTitle("提示信息"); progressDialog.setMessage("正在下载中,请稍后......"); // 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失 progressDialog.setCancelable(false); // 设置ProgressDialog样式为圆圈的形式 progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); button.setOnClickListener(new View.OnClickListener() { @Override publicvoid onClick(View v) { // 在UI Thread当中实例化AsyncTask对象,并调用execute方法 new MyAsyncTask().execute(IMAGE_PATH); } }); } /** * 定义一个类,让其继承AsyncTask这个类 * Params: String类型,表示传递给异步任务的参数类型是String,通常指定的是URL路径 * Progress: Integer类型,进度条的单位通常都是Integer类型 * Result:byte[]类型,表示我们下载好的图片以字节数组返回 *@author xiaoluo * */ publicclass MyAsyncTaskextends AsyncTask { @Override protectedvoid onPreExecute() { super.onPreExecute(); // 在onPreExecute()中我们让ProgressDialog显示出来 progressDialog.show(); } @Override protectedbyte[] doInBackground(String... params) { // 通过Apache的HttpClient来访问请求网络中的一张图片 HttpClient httpClient =new DefaultHttpClient(); HttpGet httpGet =new HttpGet(params[0]); byte[] image =newbyte[]{}; try { HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); if(httpEntity !=null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { image = EntityUtils.toByteArray(httpEntity); } } catch (Exception e) { e.printStackTrace(); } finally { httpClient.getConnectionManager().shutdown(); } return image; } @Override protectedvoid onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protectedvoid onPostExecute(byte[] result) { super.onPostExecute(result); // 将doInBackground方法返回的byte[]解码成要给Bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); // 更新我们的ImageView控件 imageView.setImageBitmap(bitmap); // 使ProgressDialog框消失 progressDialog.dismiss(); } } @Override publicboolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); returntrue; } } 我们来看看效果图: ②带有进度条更新的下载一张网络图片 下面这个代码示例,将会在下载图片的时候,显示进度条的更新,配置文件都不变,我们来看看Activity代码: publicclass MainActivityextends Activity { private Button button; private ImageView imageView; private ProgressDialog progressDialog; privatefinal String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg"; // private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg"; @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button); imageView = (ImageView)findViewById(R.id.imageView); // 弹出要给ProgressDialog progressDialog =new ProgressDialog(MainActivity.this); progressDialog.setTitle("提示信息"); progressDialog.setMessage("正在下载中,请稍后......"); // 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失 progressDialog.setCancelable(false); // 设置ProgressDialog样式为水平的样式 progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); button.setOnClickListener(new View.OnClickListener() { @Override publicvoid onClick(View v) { new MyAsyncTask().execute(IMAGE_PATH); } }); } /** * 定义一个类,让其继承AsyncTask这个类 * Params: String类型,表示传递给异步任务的参数类型是String,通常指定的是URL路径 * Progress: Integer类型,进度条的单位通常都是Integer类型 * Result:byte[]类型,表示我们下载好的图片以字节数组返回 *@author xiaoluo * */ publicclass MyAsyncTaskextends AsyncTask { @Override protectedvoid onPreExecute() { super.onPreExecute(); // 在onPreExecute()中我们让ProgressDialog显示出来 progressDialog.show(); } @Override protectedbyte[] doInBackground(String... params) { // 通过Apache的HttpClient来访问请求网络中的一张图片 HttpClient httpClient =new DefaultHttpClient(); HttpGet httpGet =new HttpGet(params[0]); byte[] image =newbyte[]{}; try { HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); InputStream inputStream =null; ByteArrayOutputStream byteArrayOutputStream =newByteArrayOutputStream(); if(httpEntity !=null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // 得到文件的总长度 long file_length = httpEntity.getContentLength(); // 每次读取后累加的长度 long total_length = 0; int length = 0; // 每次读取1024个字节 byte[] data =newbyte[1024]; inputStream = httpEntity.getContent(); while(-1 != (length = inputStream.read(data))) { // 每读一次,就将total_length累加起来 total_length += length; // 边读边写到ByteArrayOutputStream当中 byteArrayOutputStream.write(data, 0, length); // 得到当前图片下载的进度 int progress = ((int)(total_length/(float)file_length) * 100); // 时刻将当前进度更新给onProgressUpdate方法 publishProgress(progress); } } image = byteArrayOutputStream.toByteArray(); inputStream.close(); byteArrayOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } finally { httpClient.getConnectionManager().shutdown(); } return image; } @Override protectedvoid onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // 更新ProgressDialog的进度条 progressDialog.setProgress(values[0]); } @Override protectedvoid onPostExecute(byte[] result) { super.onPostExecute(result); // 将doInBackground方法返回的byte[]解码成要给Bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); // 更新我们的ImageView控件 imageView.setImageBitmap(bitmap); // 使ProgressDialog框消失 progressDialog.dismiss(); } } @Override publicboolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); returntrue; } } 我们来看看效果图: 这样我们就能够通过AsyncTask来实现从网络中下载一张图片,然后将其更新到UI控件中,并时时刻刻的更新当前的进度这个功能了。 六、AsyncTask的重要知识点 在上面两节已经详细讲解了AsyncTask的工作原理了,这里我们还要补充一下AsyncTask的一些其他知识点: 1.Cancelling a Task 我们可以在任何时刻来取消我们的异步任务的执行,通过调用 cancel(boolean)方法,调用完这个方法后系统会随后调用 isCancelled() 方法并且返回true。如果调用了这个方法,那么在 doInBackgroud() 方法执行完之后,就不会调用 onPostExecute() 方法了,取而代之的是调用 onCancelled() 方法。为了确保Task已经被取消了,我们需要经常调用 isCancelled() 方法来判断,如果有必要的话。 2.在使用AsyncTask做异步任务的时候必须要遵循的原则: AsyncTask类必须在UI Thread当中加载,在Android Jelly_Bean版本后这些都是自动完成的 AsyncTask的对象必须在UI Thread当中实例化 execute方法必须在UI Thread当中调用 不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自动调用的 AsyncTask任务只能被执行一次 到此,有关AsyncTask的总结就到此为止了,本篇随笔主要讲解了Android中的多线程知识,并且详细地讲解了 AsyncTask 异步任务的概念和实现机制,并通过实例来了解 AsyncTask 的执行过程,最后还补充了 AsyncTask 的一些重要知识点,包括如何取消一个 AsyncTask 以及,我们在使用 AsyncTask 时所必须遵循的规则。 |
1、使用优化过的数据容器。 在Android framework下,建议使用优化过的数据容器比如:SparseArray,SparseBooleanArray,LongSparseArray。通用的HashMap实现的内存使用率非常的低,因为他需要为每一个mapping创建一个分离的entry object。另外,SparseArray类避免了系统对有些key的自动装箱,因而带来了更高的效率。 2、注意内存的开销。[size=12.800000190734863px] 注意你使用的语言和第三方库的成本和开销,要自始至终的将这些因素考虑在你的程序设计中。通常,有些事情表面上看着没什么问题但实际上的开销让人惊叹。比如: ·枚举相对于静态常量来说,需要两倍甚至更多的内存。你应该完全避免在Android中使用枚举。 [size=12.666666984558105px]·每一个在java中的类(包括匿名内部类)使用大约500 bytes的代码量。 ·每一个类的实例拥有12-16 bytes的RAM消耗。 ·放置一个单独的实体到HashMap中,一个额外加的实体对象分配需要花费32 bytes。 [size=12.666666984558105px]3、关于代码的抽象 抽象是一个好的编程的基础,因为抽象可以提高代码的灵活性和可维护性。然而抽象也带来了一定的花销,一般情况下,他们有更多的代码需要执行,需要更多的时间和更多RAM来将这些代码映射到内存中。因此,如果你的抽象不能带来巨大的好处,你就应该割掉你的赘肉。 4、避免依赖注入框架 虽然注入框架给我们带来了很多方便,但是在使用这些框架的时候,框架需要花费很多时间来扫描我们自己写的代码,甚至会将哪些你根本不需要的东西也加载到内存中。 5、小心的使用扩展库 很多扩展库的代码不是针对手机环境开发的,可能在用到移动客户端的时候会导致很低的效率。因此在使用之前,需要评估一下其占用内存的大小。 即使库针对手机开发,也会有潜在的危险,因为每一个Library做的事情不尽相同。比如,一个Library使用nano protobufs而另一个使用micro protobufs。现在,在你的app中就有两个protobuf。类似情况经常发生。 6、使用混淆器移除不必要的代码 ProGuard工具通过移除无用代码,使用语意模糊来保留类,字段和方法来压缩,优化和混淆代码。可以使你的代码更加完整,更少的RAM 映射页。 7、使用多个进程(注意是process 不是 thread ok?) 如果这适合你的app,可能帮助你管理你的app的内存就是将你的app多个部分分配到多个进程中。该技术必须小心使用并且大多数应用不应该运行在多个进程下。这个技术的主要应用是后台工作跟天台工作一样重要的情况下。典型应用就是:当音乐播放器从服务器下载并长时间播放音乐,一般将其分为两个进程:一个是UI,另一个位置后台服务的运行。 like this:
[size=12.666666984558105px]process后面需要记住要有个":",这表示该进程属于你的app。 一般情况下,一块基本的空进程需要的内存大小在1.4m左右。
8、基本性能优化方法的基本原则: 1)不要做你不必要的工作; 2)不要申请不必要的内存; 例如,你明明知道一个方法返回一个String之后,你需要对这个String重新进行修改,那么就不要返回一个String,返回一个StringBuffer会是你更好的选择。 再比如,使用int比使用Integer占用更少的空间。这个大家肯定都是晓得的。 数组比一个Map拥有更好的性能。 如果你的方法不需要访问类字段,那么让你的方法是static的吧,这将会带来15%-20%速度的提升。 对于常量,请尽量使用static and final定义。如果使用final定义常量之后,会减少编译器在类生成时初始化 9、关于UI上的一些问题[size=12.800000190734863px] Hierarchy Viewer[size=12.666666984558105px] [size=12.666666984558105px]通过他,可以看到你自己的Layout文件存在的问题。你可以看到你的Layout每一部分计算,布局,渲染所需要的时间。尽量的使你的Layout扁平话,深度最好保持 在三层之内[size=12.666666984558105px] 。RelativeLayout[size=12.666666984558105px] 是解决使用LinearLayout堆叠多层问题的利剑。那些为了方便 使[size=12.666666984558105px] 用LinearLayout的layout_weight属性[size=12.666666984558105px] 的哥们,需要重点注意,这个属性真的可以减慢measure速度。所以在使用之前,一定要再三考虑,是否真的不能通过其他方法来完成你要的效果? 官方文档上 推荐使用RelativeLayout和GridLayout来避免Layout深度过深的问题[size=12.666666984558105px] 。 之前看文档,Google提供一个叫 ViewSub[size=12.666666984558105px] 的控件来优化那些不是必须要立即在UI上显示的控件,感兴趣的同学可以去看看。在API Level 1中就提供了这个东西,但是在实际开发中很少见到有人用或者提及(可能是我孤陋寡闻,公司就两个Android开发,另一个还要转IOS,我们俩的Android技术就代表了我们公司的Android技术能力,想想真悲哀!),其实蛮好用的。 重用Layout。可以使用 ListView的优化:ViewHolder的使用;AsyncTask的使用;针对ListView当前滑动状态,对图片数据的加载进行控制;(ListView在配以AsyncTask加载图片时需要注[size=12.666666984558105px] 意图片的加载完显示的位置以及图片的缓存问题,具体可以参考Google的Demo[size=12.666666984558105px] )[size=12.666666984558105px] [size=12.800000190734863px]10、将大消耗操作交给多个线程。 11、如果你的应用需要发送Broadcast但是又不希望别的应用获取到,或者你不希望处理别的应用发送的同样的action,那么请使用LocalBroadcastManager。 [size=12.666666984558105px] 该类是在Android Support v4中提供的,用来在同一个应用内的不同组件之间发送Broadcast。好处上面说了,可以保证应用的私密性。会比全局广播有更高的效率,但是官方文档没有说明具体数值。具体使用方法:
使用oc计数器的方式可以更好的控制内存 Android OnLowMemory和OnTrimMemory OnLowMemory OnLowMemory是Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。系统提供的回调有: Application.onLowMemory() Activity.OnLowMemory() Fragement.OnLowMemory() Service.OnLowMemory() ContentProvider.OnLowMemory() 除了上述系统提供的API,还可以自己实现ComponentCallbacks,通过API注册,这样也能得到OnLowMemory回调。例如: public static class MyCallback implements ComponentCallbacks { @Override public void onConfigurationChanged(Configuration arg) { } @Override public void onLowMemory() { //do release operation } } 然后,通过Context.registerComponentCallbacks ()在合适的时候注册回调就可以了。通过这种自定义的方法,可以在很多地方注册回调,而不需要局限于系统提供的组件。 OnTrimMemory OnTrimMemory是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。系统提供的回调有: Application.onTrimMemory() Activity.onTrimMemory() Fragement.OnTrimMemory() Service.onTrimMemory() ContentProvider.OnTrimMemory() OnTrimMemory的参数是一个int数值,代表不同的内存状态: TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理 TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。 TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。 TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。 以上4个是4.0增加 TRIM_MEMORY_RUNNING_CRITICAL:内存不足(后台进程不足3个),并且该进程优先级比较高,需要清理内存 TRIM_MEMORY_RUNNING_LOW:内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内存 TRIM_MEMORY_RUNNING_MODERATE:内存不足(后台进程超过5个),并且该进程优先级比较高,需要清理内存 以上3个是4.1增加 系统也提供了一个ComponentCallbacks2,通过Context.registerComponentCallbacks()注册后,就会被系统回调到。 OnLowMemory和OnTrimMemory的比较 1,OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。 2,OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。 3,通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次。 |
目前很多商业应用都会涉及到从网络上读取图片数据的问题,为了节约用户流量,应用一般会将图片缓存起来。图片缓存一般分为内存缓存和外存缓存。内存缓存运用java的缓存机制,在程序完全退出后,缓存所在的内存空间可能被其它应用程序占用从而丢失。外存缓存一般放在程序特有的访问空间或者sd卡中,在sd卡中存放的资源为公有资源,其它程序也可以访问,且对用户来讲没有一个强制清除缓存的规范机制。综合以上,本文采用将缓存图片放置在程序的特有空间中, 其它应用程序无法访问,且用户可以在应用程序管理中的"清除数据"选项中清除缓存。 本文提供三种缓存策略:(1)LRU算法,固定缓存图片数量(max_num),当图片数量超出max_num时,将缓存中最近用的最少的图片删除。(2)FTU算法,固定每张图片的缓存时限,以最后一次使用算起,超过时限后删除。(3)FMU算法,在存储器中固定一定大小的存储空间,超过固定空间后将缓存中占用最大尺寸的图片删除。使用时只需要向方法体中传递图片的URL即可。 代码片段 使用方法: 1.导入jar; 2. 获取服务; 3.提交url,交给程序去判断是否下载。 public class ImagecachetacticsdemoActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.item); /*FMU*/ imageCacheManager = ImageCacheManager.getImageCacheService(this, ImageCacheManager.MODE_FIXED_MEMORY_USED, "memory"); imageCacheManager.setMax_Memory(1024 * 1024); /*FTU*/ // imageCacheManager = ImageCacheManager.getImageCacheService(this, // ImageCacheManager.MODE_FIXED_TIMED_USED, "time"); // imageCacheManager.setDelay_millisecond(3 * 60 * 1000); /*LRU*/ // imageCacheManager = ImageCacheManager.getImageCacheService(this, // ImageCacheManager.MODE_LEAST_RECENTLY_USED, "num"); // imageCacheManager.setMax_num(5); // imageCacheManager = ImageCacheManager.getImageCacheService(this, // ImageCacheManager.MODE_NO_CACHE_USED, "nocache"); mImageView = (ImageView) findViewById(R.id.imageView); new DownloadTask() .execute("http://www.touxiang99.com/uploads/allimg/110417/1_110417112640_2.jpg"); } private class DownloadTask extends AsyncTask } |
ANR:Application NotResponding,五秒 在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。当出现下列情况时,Android就会显示ANR对话框了: 对输入事件(如按键、触摸屏事件)的响应超过5秒 意向接受器(intentReceiver)超过10秒钟仍未执行完毕 Android应用程序完全运行在一个独立的线程中(例如main)。这就意味着,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intentbroadcast)。 因此,任何运行在主线程中的方法,都要尽可能的只做少量的工作。特别是活动生命周期中的重要方法如onCreate()和 onResume()等更应如此。潜在的比较耗时的操作,如访问网络和数据库;或者是开销很大的计算,比如改变位图的大小,需要在一个单独的子线程中完成(或者是使用异步请求,如数据库操作)。但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束 -- 也不需要调用Therad.wait()或者Thread.sleep()方法。取而代之的是,主线程为子线程提供一个句柄(Handler),让子线程在即将结束的时候调用它(xing:可以参看Snake的例子,这种方法与以前我们所接触的有所不同)。使用这种方法涉及你的应用程序,能够保证你的程序对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处理而产生的ANR。这种实践需要应用到所有显示用户界面的线程,因为他们都面临着同样的超时问题。 |
|
Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。 通过Intent 可以实现各种系统组件的调用与激活. Intent filter: 可以理解为邮局或者是一个信笺的分拣系统… 这个分拣系统通过3个参数来识别 Action: 动作 view Data: 数据uri uri Category : 而外的附加信息 Action 匹配 Action 是一个用户定义的字符串,用于描述一个 Android 应用程序组件,一个 Intent Filter 可以包含多个 Action。在 AndroidManifest.xml 的 Activity 定义时可以在其 …… 如果我们在启动一个 Activity 时使用这样的 Intent 对象: Intent intent =new Intent(); intent.setAction("cn.itcast.action"); startActivity(intent); 那么所有的 Action 列表中包含了“cn.itcast”的 Activity 都将会匹配成功。 Android 预定义了一系列的 Action 分别表示特定的系统动作。这些 Action 通过常量的方式定义在 android.content. Intent中,以“ACTION_”开头。我们可以在 Android 提供的文档中找到它们的详细说明。 URI 数据匹配 一个 Intent 可以通过 URI 携带外部数据给目标组件。在 mimeType 属性指定携带外部数据的数据类型,scheme 指定协议,host、port、path 指定数据的位置、端口、和路径。如下: android:host="host" android:port="port" android:path="path"/> Intent intent = new Intent(); intent.setAction(Intent.ACTION_CALL); insent.setData( Uri.parse(tel:12345)); startAcitivty(); 电话的uri tel: 12345 http://www.baidu.com 自己定义的uri itcast://cn.itcast/person/10 如果在 Intent Filter 中指定了这些属性,那么只有所有的属性都匹配成功时 URI 数据匹配才会成功。 Category 类别匹配 默认是DEFAULT |
1.一般的基本数据类型 Intent .putextra() intent.getextra(); Parselable Serializable 2.数据的uri, intent.setData() intent.getData(); |
Android中dispatchTouchEvent,onInterceptTouchEvent, onTouchEvent的理解ec android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP android的事件处理分为3步。 1)public booleandispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent 2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent 3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent 假设当前Activity 布局如下: dispatchTouchEvent事件分发 当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent 。然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 onInterceptTouchEvent方法来决定是否要拦截这个事件, 如果onInterceptTouchEvent返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果onInterceptTouchEvent返回 false ,那么就传递给子 view,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。 如图: 事件拦截:onInterceptTouchEvent onInterceptTouchEvent用于改变事件的传递方向。决定传递方向的是返回值,返回为false时事件会传递给子控件,返回值为true时事件会传递给当前控件的onTouchEvent(),这就是所谓的Intercept(拦截)。 [tisa ps:正确的使用方法是,在此方法内仅判断事件是否需要拦截,然后返回。即便需要拦截也应该直接返回true,然后由onTouchEvent方法进行处理。] onTouchEvent用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。尤其对于ACTION_DOWN事件,返回true,表示我想要处理后续事件(ACTION_MOVE或者ACTION_UP);返回false,表示不关心此事件,并返回由父类进行处理。 在没有重写onInterceptTouchEvent()和onTouchEvent()的情况下(他们的返回值都是false), 对上面这个布局,MotionEvent事件的传递顺序如下: 当某个控件的onInterceptTouchEvent()返回值为true时,就会发生截断,事件被传到当前控件的onTouchEvent()。如我们将LayoutView2的onInterceptTouchEvent()返回值为true,则传递流程变成: 如果我们同时将LayoutView2的onInterceptTouchEvent()和onTouchEvent()设置成true,那么LayoutView2将消费被传递的事件,同时后续事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)会直接传给LayoutView2的onTouchEvent(),不传给其他任何控件的任何函数。同时传递给子空间一个ACTION_CANCEL事件。传递流程变成(图中没有画出ACTION_CANCEL事件): 小总结:onInterceptTouchEvent是自rootiew向下传递, onTouchEvent正好相反。 |
FrameLayout(帧布局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局) FrameLayout 从屏幕的左上角开始布局,叠加显示, 实际应用 播放器的暂停按钮. LinearLayout 线性布局,这个东西,从外框上可以理解为一个div,他首先是一个一个从上往下罗列在屏幕上。每一个LinearLayout里面又可分为垂直布局 (android:orientation="vertical")和水平布局(android:orientation="horizontal" )。当垂直布局时,每一行就只有一个元素,多个元素依次垂直往下;水平布局时,只有一行,每一个元素依次向右排列。 AbsoluteLayout 绝对布局犹如div指定了absolute属性,用X,Y坐标来指定元素的位置android:layout_x="20px" view android:layout_y="12px" fwvga 854*480apk qq斗地主 qq游戏大厅800*480 800*480.apk fwvga 854*480 指定平板机型的游戏开发中经常用到绝对布局 widget 绝对布局 指定机型的平板游戏开发. 2.3 3.0 1. 界面布局 任务管理器 gridview 2. 手机 任务管理 listview lephone lepad RelativeLayout 相对布局可以理解为某一个元素为参照物,来定位的布局方式。主要属性有: 相对于某一个元素 android:layout_below="@id/aaa" 该元素在 id为aaa的下面 android:layout_toLeftOf="@id/bbb" 改元素的左边是bbb 相对于父元素的地方 android:layout_alignParentLeft="true" 在父元素左对齐 android:layout_alignParentRight="true" 在父元素右对齐 Android oa客户端. TableLayout
19.AIDL的全称是什么?如何工作?
20.请解释下Android程序运行时权限与文件系统权限的区别。
|