activity:
1. 生命周期:onCreate ---> onStart(可见) ---> onResume(获取焦点) ---> onWindowFocusChanged(焦点改变监听) ---> onPause(遮住) ---> onStop(当前activity进入后台) ---> onDestory(销毁)
onRestart(重新进入前台) ---> onStart() ---> onResume() ---> onWindowFocusChanged(焦点改变监听)
判断当前的activity进入后台的方法是 onStop()
判断当前的app进入后台的方法是:
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
switch (level){
case TRIM_MEMORY_UI_HIDDEN:
LogUtils.e("当前应用程序进入了后台");
break;
}
}
2. activity的权限问题:
code检测activity的时候,如果android:exported="true"就会存在漏洞问题(只要添加了inter-filter、action之后exported会默认是true),我们可以通过给activity添加自定义的权限来解决
① 首先添加自定义的权限:<permission android:name="com.test.demo.per"/>
② 给activity添加自定义权限:android:permission="com.test.demo.per" , 并设置<category android:name="android.intent.category.DEFAULT" />
③ 在另外一个应用中添加访问权限:<uses-permission android:name="com.test.demo.per"/>
④ 通过指定的action获取类名类访问:
Intent intent = new Intent();
intent.setAction("android.intent.action.media.test");
startActivity(intent);
3. 启动模式:
standard:默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
重新startActivity(),就会重新创建一个activity; 按home键,再进去,就不会创建,走onreStart()
singleTop:可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
重新startActivity() 不在栈顶,就会重新创建一个activity; 按home键,再进去,就不会创建,走onreStart()
singleTask:只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
singleInstance:只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
会 新建一个栈 来存放这个activity的实例,下次重新激活 onNewIntent() -> onreStart() -> onResume() -> onWindowFocusChanged();
按home键,再进去,就不会创建,走onreStart(),不会显示该activity,进入的是主任务栈
4. 保存数据状态onSaveInstanceState(Bundle outState):
① 当系统内存不足时, 调用onPause()和onStop()方法后的activity可能会被系统摧毁, 此时内存中就不会存有该activity的实例对象了. 如果之后这个activity重新回到前台, 之前所作的改变就会消失.
为了避免此种情况的发生, 开发者可以覆写onSaveInstanceState()方法。
② 该方法的默认实现可以自动的保存UI控件的状态数据, android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法
③ 如果需要保存额外的数据时, 就需要覆写onSaveInstanceState()方法. 如需要保存类中成员变量的值
④ 保存持久化数据的操作应该放在onPause()中. onSaveInstanceState()方法只适合保存瞬态数据, 比如UI控件的状态, 成员变量的值等.
⑤ 除了系统处于内存不足的原因会摧毁activity之外, 某些系统设置的改变也会导致activity的摧毁和重建. 例如改变屏幕方向
if (savedInstanceState != null) {
temp = savedInstanceState.getString("temp");
}
5. 启动activity:
① 启动LAUNCHER的activity:
② 启动DEFAULT的activity:
startACtivity(Intent)
③ startactivityforresult()
6. 横竖屏切换生命周期:
① 默认情况下都会重新创建一次(网上有说竖屏-->横屏是一次,横屏-->竖屏是两次,但是测试都是一次)
② 在配置文件中给activity配置 android:configChanges="orientation|screenSize|keyboardHidden",其中"orientation|screenSize"是必须的,之后再切换屏幕,不再重新创建activity,而是走onConfigurationChanged(Configuration newConfig)方法。
Service:
1. 启动方式:
startService(intent): onCreate() ---> onstart() ---> onStartCommand()
stopService(intent): onDestory()
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.e("连接成功");
LifeService.MyBinder binder = (LifeService.MyBinder) service;
LifeService lifeService = (LifeService) binder.getService1(); //getService1()是在MyBinder里面自定义的一个方法
lifeService.testService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.e("断开连接");
}
} , Service.BIND_AUTO_CREATE);
: onCreate() ---> onBind() ---> onServiceConnected(ComponentName name, IBinder service)
不管是startService还是bindStartService都只会调用一次onCreate
2. 前台服务需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为 前台服务。
使用前台服务可以避免服务在后台运行的时候被系统KILL。
例如手机中的音乐播放器,不管手机是否是在休眠状态,只要开始播放了,系统就不会去KILL这个服务,只有当停止播放音乐时,服务才可能会回收清除。
BroadcastReceiver:
1. 动态注册广播:
DynicBroadcast dynicBroadcast = new DynicBroadcast();
IntentFilter filter = new IntentFilter();
filter.addAction("com.fanghan.broadcase.test");
registerReceiver(dynicBroadcast , filter);
在onDestory()里面再进行注销:unregisterReceiver(dynicBroadcast);
2. 静态注册广播:
<receiver
android:name=".broadcasts.StaticBroadcast"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.fanghan.broadcase.test2"/>
</intent-filter>
</receiver>
Intent intent5 = new Intent();
intent5.setAction("com.fanghan.broadcase.test2");
intent5.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendBroadcast(intent5);
注意:安卓3.1之后添加了flag,两个:intent5.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)唤醒所有广播,FLAG_EXCLUDE_STOPPED_PACKAGES不唤醒未启动的应用广播(网上是这么说的,实际代码测试还是没有启动,不知道是哪里出错了)。
3. APP应用内部广播:
//registerReceiver(mBroadcastReceiver, intentFilter);
//注册应用内广播接收器
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//unregisterReceiver(mBroadcastReceiver);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra("name", "qqyumidi");
//sendBroadcast(intent);
//发送应用内广播
localBroadcastManager.sendBroadcast(intent);
contentprovider:
1. 使用ContentProvider共享数据的好处是统一了数据访问方式。打开api系统会默认走: 空参构造--->onCreate() ,所以初始化操作最好自己创建一个有参构造函数。
2. Uri简介:这种URI由3个部分组成, “content://”, 代表数据的路径(配置文件里面的authorities属性),table的名字。示例:
content://media/internal/images 这个URI将返回设备上存储的所有图片
content://contacts/people/ 这个URI将返回设备上的所有联系人信息
content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)
private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI("com.fanghan.contentprovider.test","contact",CONTACT);
LogUtils.e("初始化了:" + uriMatcher.match(Uri.parse("content://com.fanghan.contentprovider.test/contact")));
}
创建数据库表:
create table contact(_id integer primary key autoincrement,name text not null,number text not null);
3. 获取联系人信息为例:
ContentResolver cr = context.getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if(cursor != null){
if(cursor.moveToFirst()){
while (cursor.moveToNext()){}
4. 关于Cursor的用法:
getColumnCount():返回所有列的总数
getColumnIndex(String columnName):返回指定列的名称,如果不存在返回-1
getColumnName(int columnIndex):从给定的索引返回列名
getColumnIndexOrThrow(String columnName):从零开始返回指定列名称,如果不存在将抛出IllegalArgumentException 异常
getColumnNames():返回一个字符串数组的列名
getCount():返回Cursor 中的行数
moveToFirst():移动光标到第一行
moveToLast():移动光标到最后一行
moveToNext():移动光标到下一行
moveToPosition(int position):移动光标到一个绝对的位置
moveToPrevious():移动光标到上一行
5. 关于SQLiteDatabase的用法:
SQLiteDatabase database = dbHelp.getWritableDatabase(); //可读可写
SQLiteDatabase database = dbHelp.getReadableDatabase();//仅可读
insert(String table, String nullColumnHack, ContentValues values):
table名字;当values为空时插入的字段,用null;插入的数据
query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy):
table:表名。相当于select语句from关键字后面的部分。如果是多表联合查询,可以用逗号将两个表名分开。
columns:要查询出来的列名。相当于select语句select关键字后面的部分。
selection:查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符“?”
selectionArgs:对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。
groupBy:相当于select语句group by关键字后面的部分
having:相当于select语句having关键字后面的部分
orderBy:相当于select语句order by关键字后面的部分,如:personid desc, age asc;
limit:指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。
delete(String table, String whereClause, String[] whereArgs):
update(String table, ContentValues values, String whereClause, String[] whereArgs):
6. SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型虽然只有五种,
但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型
7. 4.2之后Contentprovider默认不再有导出属性。也就是说,android:exported属性的默认值为"false"