Android基础
来源:互联网 发布:java中执行js代码 编辑:程序博客网 时间:2024/06/14 05:24
一、Activity
Activity启动模式
standard
- 每次打开activity,都在当前任务栈顶层创建新的实例,不管该activity是否已经在栈中存在。
- 都调用onCreate()方法创建实例。
- 这种模式的activity不能被其他app启动,设置export=“true”无效,强行启动app会崩溃。
singleTop
- 每次打开activity时都检测当前栈顶的activity是否是要打开的activity,
不是就调用onCreate()方法在当前任务栈中创建新的实例,
是就直接调用onNewIntent()方法打开,不再重新创建。 - 用于接收消息的情况,一次来10条消息,总不能开10个activity,一个就可以了。
这种模式的activity也不能被其他app启动,设置export=“true”无效,强行启动app会崩溃。
- 每次打开activity时都检测当前栈顶的activity是否是要打开的activity,
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销毁
- 每次打开activity时都检测当前栈中是否包含该activity,
singleInstance
该模式的activity在单独的栈中启动,并且多个app共用。- 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被销毁
- Intent.FLAG_ACTIVITY_NEW_TASK
- activity关于Task的属性—这部分没什么卵用,可以略过
- clearTaskOnLaunch
activity被激活时清空所有activity,只剩下当前activity
经测试,该属性并不能直接销毁其他activity,而是退到后台,然后重新点击桌面图标启动后,才销毁而且只对入口activity有效。所以想销毁所有activity还是自己写activityManager来管理比较靠谱。 - finishOnTaskLaunch
task被激活时销毁该activity - alwaysRetainTaskState
该属性设置为true,则该task不接受任何清理命令,一直保持当前的task状态 - 任务栈清空,意味着程序退出了。清空任务栈之后进程还保留着,就是空进程,容易被系统回收。
- clearTaskOnLaunch
Activity启动方式
- 显式启动
- 第一种
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();
常用于复杂数据选择界面的跳转,用户选择好后返回,关闭数据选择页面在之前的页面回调选择结果
- 第一种
注意
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版本已经可以在不同的任务栈之间传递数据了。具体从哪个版本开始可以传递数据的,还望读者测试告知,相互学习,共同进步。隐式启动
部分系统界面启动方式
//卸载安装包
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生命周期方法
- onCreate()//从无到有。
- onRestart()//再次从不可见到可见。
- onStart()//从不可见到可见,但是还没有到前台无法和用户交互。
- onResume()//从不交互到可交互。
- 运行了
- onPause()//从可交互到不可交互,弹出透明背景的activity时会执行该方法,activity可见但不可操作。
- onStop()//从可见到不可见,按返回键调用该方法,activity还存在于内存,下次调用无需重建。
- onDestory()//从有到无,该方法只有主动finish()或者系统内存不足时销毁activity才会调用。
Fragment生命周期方法
- onAttach()//绑定Activity,这里可以获得Activity,接口传递数据就需要用到这个方法
- onCreate()//Fragment创建
- onCreateView()//Fragment显示内容
- onActivityCreated()//此时Fragment所在的Activity已经创建完成
- onStart()//Fragment已经显示了,但是还没到前台不能交互
- onResume()//Fragment可以交互了
- 运行了
- onPause()//Fragment退到后台不能交互了
- onStop()//Fragment不显示了
- onDestroyView()//View销毁了
- onDestroy()//Fragment销毁了
- onDetach()//解绑Activity接口传递终止了
Activity和Fragment的区别
- Fragment可以直接在xml布局文件中配置,并加载显示,Activity只能代码start主动加载。
- Fragment可以灵活的替换显示界面的一部分,Activity只能覆盖整个界面。
- 可以动态加载不同Fragment
- Fragment生命周期方法多,可控性高
- Fragment切换动画丰富,Activity切换动画有限
Activity和Fragment的交互
- Fragment获取Activity数据
Activity实现Fragment内部接口,在Fragment的onAttach方法中获得接口,获取数据 - Fragment获取Activity数据
可以通过在Activity中的Fragment实例,用setArguments()方法传递数据,在Fragment中getArguments()获取数据。 - Activity获得Fragment数据
可以通过接口,设定get(Object obj)和set()方法,在Activity中实现,在get中使用obj进行处理,在set中返回activity的参数给Fragment调用。 - 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值。
系统杀死进程的顺序
先杀空进程
应用程序按返回键退出应用就是这样实现的
杀掉所有activity,剩下空进程,很快就被系统清理了再杀后台进程
进程中只有stop状态的activity,按home退出应用,这样程序在后台运行
内存不足时就被清理了再杀服务
进程中只有正在运行的服务,start方式启动的服务,不会跟activity的生命周期绑定,可以在后台稳定运行
这样内存不足时也有可能被杀死,一般内存恢复时又会被重新执行,根据参数设置进行现场恢复再杀可见进程
进程中有pause状态的activity
启动了一个透明背景的activity,上一个activity就是这种状态,可见但不能交互
内存严重不足时,会被杀死,按理说这种情形已经属于不正常状态最后杀前台进程
拥有一个与用户交互的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声明该服务是否能被其他应用调用。
启动方式
start方式启动
执行startService(intent)
如果服务没创建就先执行onCreate()创建Service再执行onStartCommand()
如果已经创建就直接执行onStartCommand()
执行stopService()
销毁Service,销毁之前生命周期方法onDestroy()会被调用
稳定运行
这种方式启动,当启动者生命周期结束时,也不会影响Service的运行,这种Service可以在后台稳定运行
即使资源不足时被杀死,资源足够时又会复活,根据onStartCommand的返回值恢复现场bind方式启动
执行bindService()
如果没有创建就先执行onCreate()创建Service再执行onBind()
执行unbindService()
如果服务是bindService()启动的,那么会先执行onUnbind()再执行onDestroy()销毁Service
如果是先startService()启动,然后再bindService()绑定的,就只执行onUnbind(),不会销毁Service
生命周期绑定
这种方式启动的Service生命周期和Activity绑定,unbindService()之后就解除了绑定关系。
这里有一个Aidl的远程访问服务的情况,用IBinder传递数据。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之前默认类型,现在基本没用了Service的运行区间
一般要监听屏幕亮灭来启动和结束service运行,达到省电的目的。其他
可以利用广播检测服务的运行状态,创建杀不死的服务
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交互数据
通过绑定服务进行交互
activity绑定Service时需要一个接口参数ServiceConnection,在activity中实现这个接口。
这个接口有两个方法,分别在绑定和解绑时被调用,绑定时会传递IBinder类型的参数过来,以获取Service传来的数据。Service实现onBind()方法,返回这个接口类型的参数。
可以通过这个接口传递Service实例本身,这样activity就可以随心所欲调用Service的方法,这样可以用service实例设置另外的接口传递给service本身,从而实现在activity中使用service的数据进行界面更新。
同时可以用得到的service实例,把activity实例设置给service,这样service就可以随心所欲调用的activity的方法。可以通过广播进行数据交互
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
- 定义实现
定义类,继承BroadcastReceiver,重写onReceive()方法,这个方法中进行接收到广播后的处理。 启动
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来过滤接收的广播类型广播类型
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" />
系统广播
"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
简介
可以将应用中的数据对外进行共享
数据访问方式统一,不必针对不同数据类型采取不同的策略
将数据封装,只暴露出我们希望提供给其他程序的数据
数据更新可被监听配置
<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-->
匹配
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));}
数据操作方法
//增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;}
注册监听器
getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) { public void onChange(boolean selfChange) { //TODO收到数据改变,在此做出相应处理。 }});
事务操作
//添加事务支持//复写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);
其他
String getType()//这个方法一般不用,返回null即可。在这里解释一下。
vnd.android.cursor.item/single //查询一条数据返回这个类型,single自定义
vnd.android.cursor.dir/multi //查询多条数据返回这个类型,multi自定义ContentValues实现方式是一个HashMap,特点是key都是String
BaseNameValuePair的key和value都是String
五、常用数据存储方式
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, "");}
文件存取数据
两个方法
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"/>
- xml存取
待续。。。 - 数据库存储
参考关于数据库 - 网络存取
待续。。。
六、ListView的优化
待续。。。
七、动画
待续。。。
八、事件传递
点这里
九、内存优化
待续。。。
十、UI优化
待续。。。
十一、适配
点这里
十二、单元测试
待续。。。
- android基础--activity基础
- 【Android基础】AsyncTask基础
- Android基础
- android基础
- Android基础
- Android基础
- android 基础
- android基础
- android基础
- android基础
- android基础
- android基础
- android基础
- android基础
- android基础
- android基础
- Android基础
- android 基础
- PostgreSQL字符集问题
- springIOC源码解析(六)
- java中返回任意类型值( <V> V get(Object obj))
- 鼓励别人谈论他们自己
- 表格each的使用,获得某一列叠加结果,,摆脱出现的循环判断错误和类型错误 nan
- Android基础
- android程序员的chrome教程
- Android 自定义View(三)Material Design风格的ProgressBar
- 解决Spring Web MVC中POST中文乱码问题
- picasso的引用方法以及使用方法
- Failed to complete Gradle execution
- qduoj 交通规划
- 让别人感到他们自己很重要
- Android Studio工程目录结构介绍