Android基础

来源:互联网 发布:java中执行js代码 编辑:程序博客网 时间:2024/06/14 05:24

一、Activity

Activity启动模式

  1. standard

    • 每次打开activity,都在当前任务栈顶层创建新的实例,不管该activity是否已经在栈中存在。
    • 都调用onCreate()方法创建实例。
    • 这种模式的activity不能被其他app启动,设置export=“true”无效,强行启动app会崩溃。
  2. singleTop

    • 每次打开activity时都检测当前栈顶的activity是否是要打开的activity,
      不是就调用onCreate()方法在当前任务栈中创建新的实例,
      是就直接调用onNewIntent()方法打开,不再重新创建。
    • 用于接收消息的情况,一次来10条消息,总不能开10个activity,一个就可以了。
      这种模式的activity也不能被其他app启动,设置export=“true”无效,强行启动app会崩溃。
  3. singleTask

    • 每次打开activity时都检测当前栈中是否包含该activity,
      不包含就调用onCreate()方法在当前任务栈中创建新的实例,
      包含就把该activity上边的activity都销毁,并调用onNewIntent()方法打开该activity。
    • 其他app启动该activity时,新建任务栈。(需要设置export=“true”)
      如果该activity已经被启动在某个任务栈中了,那个该任务栈被带到前台,销毁上边的activity,并调用onNewIntent()方法。
    • 可以将主activity设置为该模式,用于退出整个app应用
      将要退出的activity转到主activity,这样就可以将主activity以上的activity都销毁
      然后重写主activity中的onNewIntent()方法,执行finish()将最后的activity销毁
  4. singleInstance
    该模式的activity在单独的栈中启动,并且多个app共用。

  5. Flag设置启动模式
    • Intent.FLAG_ACTIVITY_NEW_TASK
      启动activity时需要创建新的任务栈,通常用于service或BroadcastReceiver中启动activity,
      因为service和BroadcastReceiver的Context中没有activity栈,需要新建任务栈。
      实际测试得知这个新建任务栈并非真的新建,如果已经有任务栈存在了,会首先考虑在已经存在的任务栈中创建该activity。
    • Intent.FLAG_ACTIVITY_SINGLE_TOP
      相当于singleTop模式
    • Intent.FLAG_ACTIVITY_CLEAR_TOP
      相当于singleTask
    • Intent.FLAG_ACTIVITY_NO_HISTORY
      启动另一个activity后该activity被销毁
  6. activity关于Task的属性这部分没什么卵用,可以略过
    • clearTaskOnLaunch
      activity被激活时清空所有activity,只剩下当前activity
      经测试,该属性并不能直接销毁其他activity,而是退到后台,然后重新点击桌面图标启动后,才销毁而且只对入口activity有效。所以想销毁所有activity还是自己写activityManager来管理比较靠谱。
    • finishOnTaskLaunch
      task被激活时销毁该activity
    • alwaysRetainTaskState
      该属性设置为true,则该task不接受任何清理命令,一直保持当前的task状态
    • 任务栈清空,意味着程序退出了。清空任务栈之后进程还保留着,就是空进程,容易被系统回收。

Activity启动方式

  1. 显式启动
    • 第一种
      Intent intent = new Intent(ActivityA.this, ActivityB.class);
      startActivity(intent);
      最常用, 正常启动当前app中的另一个Activity都这样启动
    • 第二种
      Intent intent = new Intent();
      intent.setClassName(“com.ghc.app”,”com.ghc.app.ActivityA”);
      startActivity(intent);
      启动另一个app种的activity或者系统的activity唱这样启动
    • 第三种
      启动
      Intent intent = new Intent(ActivityA.this, ActivityB.class);
      startActivityForResult(intent, requestCode);
      需要
      ActivityA中重写onActivityResult()方法用于回调。
      还需要
      ActivityB中执行如下方法
      intent = new Intent();
      intent.putExtra(getString(R.string.data), getString(R.string.data));
      setResult(RESULT_OK, intent);
      finish();
      常用于复杂数据选择界面的跳转,用户选择好后返回,关闭数据选择页面在之前的页面回调选择结果
  2. 注意
    Android开发者认为不同任务栈之间不能传递数据,所以singleInstance模式的activity通过startActivityForResult方式被启动或者启动别的activity都会出现问题,经测试结果是这样的(其中activityD是singleInstance模式,activityA是正常模式,a启动d):

    11-10 10:20:16.133 3033-3033/com.hcsys E/activitytest: ActivityA------onPause11-10 10:20:16.133 3033-3033/com.hcsys E/activitytest: ActivityA------onActivityResult--/011-10 10:20:16.133 3033-3033/com.hcsys E/activitytest: ActivityA------onResume11-10 10:20:16.133 3033-3033/com.hcsys E/activitytest: ActivityA------onPause11-10 10:20:16.173 3033-3033/com.hcsys E/activitytest: ActivityD------onCreate11-10 10:20:16.173 3033-3033/com.hcsys E/activitytest: ActivityD------onStart11-10 10:20:16.173 3033-3033/com.hcsys E/activitytest: ActivityD------onResume11-10 10:20:16.263 3033-3033/com.hcsys E/activitytest: ActivityA------onSaveInstanceState

    分析上述log得出结论,第一次启动D失败,直接返回RESULT_CANCELED回到A,然后再以start方式启动D
    经测试4.04是这样的结果,5.11已经没有了,也就是5.11版本已经可以在不同的任务栈之间传递数据了。具体从哪个版本开始可以传递数据的,还望读者测试告知,相互学习,共同进步。

  3. 隐式启动

    • 部分系统界面启动方式
      //卸载安装包
      Intent intent = new Intent(Intent.ACTION_DELETE,Uri.parse(“package:com.hcsys.testservice”));
      Intent intent = new Intent();
      //安装apk
      intent.setAction(“android.intent.action.INSTALL_PACKAGE” );
      intent.addCategory(“android.intent.category.DEFAULT”);
      intent.setDataAndType(Uri.fromFile(t), “application/vnd.android.package-archive”);
      //打电话
      intent.setAction(Intent.ACTION_CALL);
      intent.setData(Uri.parse(“tel//123456”));
      //打开网页
      intent.setAction(Intent.ACTION_VIEW);
      intent.setData(Uri.parse(“www.baidu.com”));
      //打开音频视频
      intent.setAction(Intent.ACTION_VIEW);
      intent.setDataAndType(Uri.parse(“file://mnt/sdcard/music.mp3”),”audio/*”);
      //显示标识为1的联系人信息
      ACTION_VIEW content://com.android.contacts/contacts/1
      //显示标识为1的联系人编辑界面
      ACTION_EDIT content://com.android.contacts/contacts/1
      //显示标识为1的联系人拨号界面
      ACTION_DIAL content://com.android.contacts/contacts/1
      //显示向指定号码123拨号的界面
      ACTION_VIEW tel:123
      ACTION_DIAL tel:123
      //显示所有联系人列表的信息
      ACTION_DIAL content://contacts/people/

    • 自定义隐式启动activity配置

    <activity   android:name="com.hcsys.activity.OtherActivity"   android:label="OtherActivity" >   <!-- 配置隐式意图,匹配http -->   <intent-filter>       <action android:name="android.intent.action.VIEW" /><!--表示动作为View -->       <data android:scheme="http" /><!--http开头-->       <category android:name="android.intent.category.DEFAULT" /> <!--表示启动时,默认匹配 -->   </intent-filter>   <!-- 匹配tel -->    <intent-filter>       <action android:name="android.intent.action.CALL" /><!--表示动作为Call -->           <data android:scheme="tel" /><!--tel开头-->       <category android:name="android.intent.category.DEFAULT" /> <!--表示启动时,默认匹配 -->   </intent-filter>   <!-- 匹配 音频、视频 -->   <intent-filter>       <action android:name="android.intent.action.VIEW" /><!--表示动作为View -->       <data android:scheme="file" android:mimeType="audio/*" /><!--file开头,文件协议l类型 -->       <data android:scheme="file" android:mimeType="video/*" /><!--file开头,文件协议l类型 -->       <category android:name="android.intent.category.DEFAULT" /><!--表示启动时,默认匹配 -->     </intent-filter></activity>
    • 隐式启动数据传递
      获取隐式启动时用setData()方法传递的数据,getIntent().getData();
    • 创建Activity的快捷方式
    Intent intent = new Intent();intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");// 指定名称intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "TestDemo");// 指定图标intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);// 指定行为Intent clickIntent = new Intent(this, SplashActivity.class);intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, clickIntent);sendBroadcast(intent);//权限:<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
    • 其他
      //flags,设置为 MATCH_DEFAULT_ONLY, 这样,就仅仅匹配那些在category中声明了DEFAULT的activity
      //返回所有匹配的activity信息
      List queryIntentActivities(Intent intent, int flags);
      //返回最佳匹配的activity信息
      ResolveInfo resolveActivity(Intent intent, int flags);

Activity生命周期方法

  1. onCreate()//从无到有。
  2. onRestart()//再次从不可见到可见。
  3. onStart()//从不可见到可见,但是还没有到前台无法和用户交互。
  4. onResume()//从不交互到可交互。
  5. 运行了
  6. onPause()//从可交互到不可交互,弹出透明背景的activity时会执行该方法,activity可见但不可操作。
  7. onStop()//从可见到不可见,按返回键调用该方法,activity还存在于内存,下次调用无需重建。
  8. onDestory()//从有到无,该方法只有主动finish()或者系统内存不足时销毁activity才会调用。

Fragment生命周期方法

  1. onAttach()//绑定Activity,这里可以获得Activity,接口传递数据就需要用到这个方法
  2. onCreate()//Fragment创建
  3. onCreateView()//Fragment显示内容
  4. onActivityCreated()//此时Fragment所在的Activity已经创建完成
  5. onStart()//Fragment已经显示了,但是还没到前台不能交互
  6. onResume()//Fragment可以交互了
  7. 运行了
  8. onPause()//Fragment退到后台不能交互了
  9. onStop()//Fragment不显示了
  10. onDestroyView()//View销毁了
  11. onDestroy()//Fragment销毁了
  12. onDetach()//解绑Activity接口传递终止了

Activity和Fragment的区别

  1. Fragment可以直接在xml布局文件中配置,并加载显示,Activity只能代码start主动加载。
  2. Fragment可以灵活的替换显示界面的一部分,Activity只能覆盖整个界面。
  3. 可以动态加载不同Fragment
  4. Fragment生命周期方法多,可控性高
  5. Fragment切换动画丰富,Activity切换动画有限

Activity和Fragment的交互

  1. Fragment获取Activity数据
    Activity实现Fragment内部接口,在Fragment的onAttach方法中获得接口,获取数据
  2. Fragment获取Activity数据
    可以通过在Activity中的Fragment实例,用setArguments()方法传递数据,在Fragment中getArguments()获取数据。
  3. Activity获得Fragment数据
    可以通过接口,设定get(Object obj)和set()方法,在Activity中实现,在get中使用obj进行处理,在set中返回activity的参数给Fragment调用。
  4. Activity获得Fragment数据
    Activity中就有Fragment实例,可以直接调用Fragment的方法获取数据

Activity在清单文件中的其他配置

  • 三星拍照失败问题,加如下配置解决,旋转屏幕不会重新创建Activity,而是执行onConfigChanged()方法。
    android:configChanges=”orientation|screenSize”
  • 保持竖屏
    android:screenOrientation=”portrait”
  • 调整键盘自适应界面
    android:windowSoftInputMode=”stateHidden|adjustPan”
    android:windowSoftInputMode=”stateVisible|adjustUnspecified”

保存恢复数据

  • 保存数据
    protected void onSaveInstanceState(Bundle outState)
    此方法在activity有被销毁的可能性时就会执行,如启动了其他activity,按home键退到后台等
    另注:通知栏下拉不影响activity生命周期
  • 恢复数据
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    该方法当activity真的因内存不足或者屏幕旋转被销毁时才会执行,onCreate(Bundle savedInstanceState)方法中这个savedInstanceState参数是一样的,都是onSaveInstanceState(Bundle outState)中保存的outState值。

系统杀死进程的顺序

  1. 先杀空进程
    应用程序按返回键退出应用就是这样实现的
    杀掉所有activity,剩下空进程,很快就被系统清理了

  2. 再杀后台进程
    进程中只有stop状态的activity,按home退出应用,这样程序在后台运行
    内存不足时就被清理了

  3. 再杀服务
    进程中只有正在运行的服务,start方式启动的服务,不会跟activity的生命周期绑定,可以在后台稳定运行
    这样内存不足时也有可能被杀死,一般内存恢复时又会被重新执行,根据参数设置进行现场恢复

  4. 再杀可见进程
    进程中有pause状态的activity
    启动了一个透明背景的activity,上一个activity就是这种状态,可见但不能交互
    内存严重不足时,会被杀死,按理说这种情形已经属于不正常状态

  5. 最后杀前台进程
    拥有一个与用户交互的Activity
    拥有一个执行了startForeground()方法的Service
    拥有一个正在执行onReceive()方法的BroadcastReceiver
    等等

二、Service

配置说明

定义类继承Service,清单文件中声明

<service  android:name="com.ixintui.push.PushService"  android:exported="true"  android:process=":ixintui_service_v1" />

process声明该服务在独立的进程进行,进程名是”:ixintui_service_v1”
独立进程可单独分配内存,减少内存溢出的几率。例如,图片处理比较占内存,就可以在独立的进程进行。
exported声明该服务是否能被其他应用调用。

启动方式

  1. start方式启动
    执行startService(intent)
    如果服务没创建就先执行onCreate()创建Service再执行onStartCommand()
    如果已经创建就直接执行onStartCommand()
    执行stopService()
    销毁Service,销毁之前生命周期方法onDestroy()会被调用
    稳定运行
    这种方式启动,当启动者生命周期结束时,也不会影响Service的运行,这种Service可以在后台稳定运行
    即使资源不足时被杀死,资源足够时又会复活,根据onStartCommand的返回值恢复现场

  2. bind方式启动
    执行bindService()
    如果没有创建就先执行onCreate()创建Service再执行onBind()
    执行unbindService()
    如果服务是bindService()启动的,那么会先执行onUnbind()再执行onDestroy()销毁Service
    如果是先startService()启动,然后再bindService()绑定的,就只执行onUnbind(),不会销毁Service
    生命周期绑定
    这种方式启动的Service生命周期和Activity绑定,unbindService()之后就解除了绑定关系。
    这里有一个Aidl的远程访问服务的情况,用IBinder传递数据。

  3. onStartCommand()返回值
    START_STICKY
    如果service进程被kill掉,可恢复但不保留递送的intent对象。
    kill掉以后系统会尝试重新创建service。创建后会执行该方法onStartCommand(Intent,int,int)。
    如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
    用startService(intent)传递命令参数。
    api5包括api5之后默认类型
    START_NOT_STICKY
    使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
    START_REDELIVER_INTENT
    重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值重新传入。
    START_STICKY_COMPATIBILITY
    START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
    api5之前默认类型,现在基本没用了

  4. Service的运行区间
    一般要监听屏幕亮灭来启动和结束service运行,达到省电的目的。

  5. 其他
    可以利用广播检测服务的运行状态,创建杀不死的服务
    Intent.ACTION_TIME_TICK,这个广播每分钟发送一次,可以每分钟检测一次服务是否在运行,如果没有就重新启动服务。
    服务常被用来检测sd卡、多媒体播放、记录地理位置信息改变等等

    //判断服务是否已经启动//得到组件管理者ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);//得到运行的服务列表List<RunningServiceInfo> list = am.getRunningServices(Integer.MAX_VALUE);//遍历已启动的服务列表for (RunningServiceInfo info : list) {    //获取服务的ComponentName对象    ComponentName service = info.service;    String className = service.getClassName();    if (className.equals(clazz.getName())) {        return true;    }}return fasle;

Service和Activity交互数据

  1. 通过绑定服务进行交互
    activity绑定Service时需要一个接口参数ServiceConnection,在activity中实现这个接口。
    这个接口有两个方法,分别在绑定和解绑时被调用,绑定时会传递IBinder类型的参数过来,以获取Service传来的数据。Service实现onBind()方法,返回这个接口类型的参数。
    可以通过这个接口传递Service实例本身,这样activity就可以随心所欲调用Service的方法,这样可以用service实例设置另外的接口传递给service本身,从而实现在activity中使用service的数据进行界面更新。
    同时可以用得到的service实例,把activity实例设置给service,这样service就可以随心所欲调用的activity的方法。

  2. 可以通过广播进行数据交互

Service和Thread的区别

  • Servie是系统的组件,它由系统进程托管(ServiceManager);
    它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是Binder,它是在linux层交换信息的一种ipc。
  • Thread是由本应用程序托管。
  • Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
  • Service 是android的一种机制
    如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。
    如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。
  • 精彩分析
    Service是运行在主线程里的,也就是说如果你在Service里编写了非常耗时的代码,程序必定会出现ANR的。

    你可能会惊呼,这不是坑爹么!?那我要Service又有何用呢?其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

    额,既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

三、BroadcastReceiver

  1. 定义实现
    定义类,继承BroadcastReceiver,重写onReceive()方法,这个方法中进行接收到广播后的处理。
  2. 启动
    a.在清单文件中配置启动
    这种配置方式,默认需要程序安装后启动一次,之后就一直可以接收,不管程序是否运行

    <receiver android:name="com.ghc.CallReceiver" >  <intent-filter><!--监听电话拨出事件-->      <action android:name="android.intent.action.NEW_OUTGOING_CALL" />  </intent-filter></receiver>

    b.代码启动
    这种方式只在代码生命周期内能接收广播,生命周期结束就不能接收了,要unRegister()释放资源。
    registerReceiver(BroadcastReceiver, IntentFilter);//通过IntentFilter来过滤接收的广播类型

  3. 广播类型
    a.随机广播
    用sendBroadcast(intent)方法发送广播,通过intent的action指定广播接收者的类型。
    发送广播可以携带数据,所有接收者都能收到数据,数据不能被修改,不会中断。接收者无序。
    发送广播和接收广播都可以设置权限,以过滤广播接收情况。
    发广播代码:
    Intent intent = new Intent(context.getPackageName() + UIBroadcastReceiver.class.getName());
    intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);//未启动过的应用也可以接收该广播,默认不可以。
    intent.putExtra(ACTION_KEY, action); 或 intent.putExtras(bundle);
    sendBroadcast(intent);

    b.有序广播
    通过abortBroadcast()方法可以中断广播,使广播不再继续传递。
    接受者之间按照优先级可以进行数据传递。可以设置Priority的值进行优先级配置。
    sendOrderedBroadcast(intent,”com.ghc.permission.broadcast.RECEIVE”); //发送有序广播
    abortBroadcast();// 中断有序广播,优先级更低的接收者就接收不到广播了
    setResultData(null);//有序广播中修改传到下一个广播中的值
    //指定广播接收者,这样的广播接收者不需要配置,不需要权限,不会被中断,一定能接收到广播
    sendOrderedBroadcast(intent,
    “com.ghc.permission.broadcast.RECEIVE”,
    new CReceiver(), null, 1, “MainActivity”, bundle);

    c.异步广播
    sendStickyBroadcast(intent);
    如果用sendBroadcast发送广播,context不处于onResume状态就接收不到广播,即使receiver所在context重新处于onResume状态也不能收到广播。
    而sendStickyBroadcast发送的就可以在恢复onResume状态时收到广播。
    去掉是用这个方法removeStickyBroadcast(intent);

    <!--这个广播需要权限--><uses-permission android:name="android.permission.BROADCAST_STICKY" />
  4. 系统广播

    "android.provider.Telephony.SMS_RECEIVED" //接收短信广播< uses-permission android:name = "android.permission.RECEIVE_SMS" /><!--接收短信权限 -->< uses-permission android:name = "android.permission.SEND_SMS" /><!--发送短信权限 -->"android.intent.action.PHONE_STATE" //来电状态监听广播<uses-permission android:name="android.permission.READ_PHONE_STATE"/><!--来电状态监听权限 -->"android.intent.action.BOOT_COMPLETED"//监听开机广播<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><!--监听开机权限 -->"android.net.conn.CONNECTIVITY_CHANGE"//网络状态改变广播<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><!--网络状态获取权限 -->"android.intent.action.BATTERY_CHANGED"//电量变化广播"android.intent.action.NEW_OUTGOING_CALL"//拨打电话广播

四、ContentProvider

  1. 简介
    可以将应用中的数据对外进行共享
    数据访问方式统一,不必针对不同数据类型采取不同的策略
    将数据封装,只暴露出我们希望提供给其他程序的数据
    数据更新可被监听

  2. 配置

    <provider  android:name=".common.database.provider.HcsysProvider"  android:authorities="${applicationId};hcsys"  android:exported="false" /><!--${applicationId};hcsys表示,可以用${applicationId}或者hcsys做authorithies--><!--完整uri:content://${applicationId}/study 或 content://hcsys/study-->
  3. 匹配

    private UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(HcsysAuthority.AUTHORITY, uriInfo.getPath(), uriInfo.getCode());//path的位置#代表数字,*代表文本//path对应着不同的表,不同的codeswitch (uriMatcher.match(uri)) {// 匹配uri    case CODE:        //TODO数据操作    default:        throw new IllegalArgumentException(String.format("Uri:%s 不是合法的uri地址", uri));}
  4. 数据操作方法

    //增Uri insert(){    String talbeName = getTableNameByCode(uri);  if (talbeName == null) {      throw new IllegalArgumentException(UNKNOW_URI + uri);  }  long rowId = sqlDB.insert(talbeName, talbeName, contentValues);  if (rowId > 0) {      Uri ret = ContentUris.withAppendedId(uri, rowId);      getContext().getContentResolver().notifyChange(ret, null);      return ret;  }  throw new SQLException("Failed to insert row into " + uri);}//删int delete(){    String talbeName = getTableNameByCode(uri);  if (talbeName == null) {      throw new IllegalArgumentException(UNKNOW_URI + uri);  }  int count = sqlDB.delete(talbeName, s, strings);  if (count > 0) {      getContext().getContentResolver().notifyChange(uri, null);  }  return count;}//改int update(){    String talbeName = getTableNameByCode(uri);  if (talbeName == null) {      throw new IllegalArgumentException(UNKNOW_URI + uri);  }  int count = sqlDB.update(talbeName, contentValues, s, strings);  if (count > 0) {      getContext().getContentResolver().notifyChange(uri, null);  }  return count;}//查Cursor query(){    String talbeName = getTableName(uri);    if (talbeName == null) {        throw new IllegalArgumentException(UNKNOW_URI + uri);    }    SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();    sqb.setTables(talbeName);    Cursor c = sqb.query(sqlDB, projection, selection, selectionArgs, null, null, sortOrder);    c.setNotificationUri(getContext().getContentResolver(), uri);    return c;}
  5. 注册监听器

    getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {    public void onChange(boolean selfChange) {                                      //TODO收到数据改变,在此做出相应处理。    }});
  6. 事务操作

    //添加事务支持//复写applyBatch()方法,内容如下sqlDB.beginTransaction();//开始事务try {    ContentProviderResult[] results = super.applyBatch(operations);    sqlDB.setTransactionSuccessful();//设置事务标记为successful    return results;} finally {    sqlDB.endTransaction();//结束事务}//使用事务处理数据ArrayList<ContentProviderOperation>ops = new ArrayList<ContentProviderOperation>();  //添加一个删除Person表的操作 ops.add(ContentProviderOperation.newDelete(Person.CONTENT_URI).build());//添加一条记录到Home表ops.add(ContentProviderOperation.newInsert(Home.CONTENT_URI).withValues(values).build());//处理事务 getContentResolver().applyBatch(PROVIDER.AUTHORITY,ops);
  7. 其他
    String getType()//这个方法一般不用,返回null即可。在这里解释一下。
    vnd.android.cursor.item/single //查询一条数据返回这个类型,single自定义
    vnd.android.cursor.dir/multi //查询多条数据返回这个类型,multi自定义

    ContentValues实现方式是一个HashMap,特点是key都是String
    BaseNameValuePair的key和value都是String

五、常用数据存储方式

  1. SharePreference
    保存少量基本数据类型,boolean,int,float,long和String五种。
    如,软件配置里的是否开启声音提示,是否需要自动检测设备,access_token、user、是否第一次登录等等。
    对于像user这样的复杂数据类型可以通过Gson()完成json和字符串之间的转化,或者通过base64加密算法完成user和字符串之间的转化,然后sharepreference对字符串进行操作,当然后者需要user实现Serializable接口。实例代码有很多,自行百度即可。
    下面仅列出sharepreference基本的使用方法。

    //路径/data/data/<package name>/shared_prefssharePreference = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);//Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。//Context.MODE_WORLD_READABLE:  指定该SharedPreferences数据能被其他应用程序读,但不能写。//Context.MODE_WORLD_WRITEABLE:  指定该SharedPreferences数据能被其他应用程序读,写public void setValue(String name, String value) {    SharedPreferences.Editor edit = sharePreference.edit();    edit.putString(name, value);    edit.commit();//在主线程使用不关心返回值,可以使用edit.apply();}public String getStringValue(String name) {    return sharePreference.getString(name, "");}
  2. 文件存取数据
    两个方法
    context.openFileInputStream(String fileName);
    context.openFileOutputStream(String fileName, int mode);

    mode介绍
    MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,写入的内容会覆盖原文件的内容。
    MODE_APPEND:检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
    MODE_WORLD_READABLE:表示当前文件可以被其他应用读取。
    MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。

    重要方法
    //在应用程序的数据文件夹下(即 /data/data/包名/)获取或者创建app_name对应的子目录,只能创建一级目录
    File getDir(String name , int mode);
    //获取该应用程序的数据文件夹(即 /data/data/包名/files)
    File getFilesDir();
    //获取该应用程序的缓存文件夹(即 /data/data/包名/cache)
    File getCacheDir();
    //返回该应用数据文件夹的全部文件( 即 /data/data/包名/files/ 目录下的所有文件名)
    String[] fileList();
    //返回该应用在sd卡缓存目录(即 /storage/emulated/0/Android/data/包名/cache)
    File getExternalCacheDir();
    //返回该应用在sd卡缓存目录(即 /storage/emulated/0/Android/data/包名/files)
    File getExternalFilesDir(null);

    sd卡权限

    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  3. xml存取
    待续。。。
  4. 数据库存储
    参考关于数据库
  5. 网络存取
    待续。。。

六、ListView的优化

待续。。。

七、动画

待续。。。

八、事件传递

点这里

九、内存优化

待续。。。

十、UI优化

待续。。。

十一、适配

点这里

十二、单元测试

待续。。。

1 0