public class BroadcastReceiverTest extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //拿到我们注册的广播类型 String action = intent.getAction(); Log.d("TAG",action); //TODO 处理自己逻辑... } }注意:Android对广播接受者中的onReceive()方法执行时间有限制,因此我们在onReceive中不能做一些非常耗时的操作,如果超过了规定的时间,会自动跳出onReceive方法的执行。
<receiver android:name=".BroadcastReceiverTest"> <!-- 订阅我们这个广播接收者感兴趣的广播类型 --> <intent-filter > <action android:name="com.scu.lly.action.MyBROADCAST"/> <action android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver>其中这个“com.scu.lly.action.MyBROADCAST”是我们自定义的一个广播类型,以后只要有地方通过sendBroadcast(Intent)发送出该类型的广播,我们的BroadcastReceiverTest就能够接收到。当然,我们也可以订阅一些Android系统内置的广播,比如开机启动广播,如下:
<receiver android:name=".BroadcastReceiverTest"> <!-- 订阅我们这个广播接收者感兴趣的广播类型 --> <intent-filter > <!-- 这是我们自定义的广播类型 --> <action android:name="com.scu.lly.action.MyBROADCAST"/> <!-- 这是我们系统的开机广播 --> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver>我们订阅了两个感兴趣的广播,不论发出了哪一个广播我们都能够接收到,此时,只需要在onReceive()判断一下,当前是哪种广播类型即可,如下:
public class BroadcastReceiverTest extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //拿到我们注册的广播类型 String action = intent.getAction(); Log.d("TAG",action); if("com.scu.lly.action.MyBROADCAST".equals(action)){//处理我们接收到的自定义的广播 //TODO }else if("android.intent.action.BOOT_COMPLETED".equals(action)){//处理开机广播 //TODO } } }注意:静态注册这种方式是常驻型的,即使我们的应用程序退出了,如果系统中有注册的广播类型发出,我们自定义的BroadcastReceiverTest 也会被系统调用自动运行。比如,开机广播,当我们重启手机时,虽然此时我们的应用还未启动,但是BroadcastReceiverTest 也能够接收到该广播。
BroadcastReceiverTest receiver = new BroadcastReceiverTest(); IntentFilter filter = new IntentFilter(); filter.addAction("com.scu.lly.action.MyBROADCAST"); registerReceiver(receiver, filter);注意:动态注册的广播接收者不是常驻型的,它会跟随Activity或Service的生命周期,因此,在Activity或Service的onDestroy()方法中需要解除注册。否则,系统会提示异常信息。如下:
@Override public void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); }两种注册方式的区别: (1)注册方式不一样,静态注册是在AndroidMnifest.xml中配置,动态注册是在代码中设置; (2)生命周期不同,静态注册是常驻型的,即使应用程序退出,也能够收到发出的广播,而动态注册会跟随所依附的Activity或Service的生命周期,需要在onDestroy()中解除注册。因此,静态注册适合注册一些系统广播,而代码注册适合注册一些开发者自定义的一些广播; (3)功能受限方面,有些系统广播只能够通过静态注册的方式注册该广播类型,通过动态注册不能够接收到广播信息,比如,监听电池电量的广播、网络状态变化的广播...。
Intent intent = new Intent("com.scu.lly.action.MyBROADCAST");//发送指定类型的广播 intent.putExtra("name", "lly");//在发送广播的同时,还可以携带一些参数过去 sendBroadcast(intent);广播的种类: 一个广播,可以被多个注册了该广播的广播接收者接收,通过sendBoroadcast()方式发送出的广播,多个广播接收者之间都可以接收到该广播,并且不会相互影响。如果要定义这些广播接收者之间的接收顺序,可以使用有序广播。
public class BroadcastReceiverTest extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //拿到我们注册的广播类型 String action = intent.getAction(); Log.d("TAG",action); if("com.scu.lly.action.MyBROADCAST".equals(action)){//处理我们接收到的自定义的广播 //往下一级传递时,将这里的处理结果传过去 Bundle bundle = new Bundle(); bundle.putString("result", "resultinfo"); setResultExtras(bundle); // abortBroadcast(); } } } public class BroadcastReceiverTest2 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //拿到我们注册的广播类型 String action = intent.getAction(); Log.d("TAG",action); if("com.scu.lly.action.MyBROADCAST".equals(action)){//处理我们接收到的自定义的广播 //拿到上一级接收者的处理结果 Bundle bundle = getResultExtras(true); String res = bundle.getString("result"); Log.d("TAG", res); } } }上面我们通过setResultExtras和getResultExtras的方式,在广播接收者之间传递了参数,根据具体需求可要可不要。 在广播注册的地方,我们再定义广播接收者之间的优先级:
<receiver android:name=".BroadcastReceiverTest"> <intent-filter android:priority="100"> <!-- 这是我们自定义的广播类型 --> <action android:name="com.scu.lly.action.MyBROADCAST"/> <action android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver> <receiver android:name=".BroadcastReceiverTest2"> <intent-filter android:priority="90"> <!-- 这是我们自定义的广播类型 --> <action android:name="com.scu.lly.action.MyBROADCAST"/> <action android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver>注册好了之后,我们通过sendOrderedBroadcast方式发送有序广播,如下:
Intent intent = new Intent("com.scu.lly.action.MyBROADCAST"); intent.putExtra("name", "lly");//在发送广播的同时,还可以携带一些参数过去 sendOrderedBroadcast(intent, "com.scu.lly.permission.MyBROADCAST_PERMISSION");通过sendOrderedBroadcast方式发送有序广播,第二个参数为permission权限参数, <1>可以为null,表示注册该广播时不需要在AndroidManifest.xml中声明指定权限; <2>不为null,那么,我们在注册该广播时,需要在AndroidManifest.xml中声明该指定的权限;这种方式更加安全,特别是针对一些隐私广播,比如我们在注册监听电话来电,读取短信的广播时,都需要在AndroidManifest.xml中声明相应的权限。
SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE);//第一步 SharedPreferences.Editor editor = settings.edit();//第二步 editor.putString("name", "lly");//第三步 editor.commit();//第四步SharedPreference的使用主要就是上面四步: 第一步,通过Context拿到SharedPreferences对象,其中getSharedPreferences(String fileName,int mode),第一个参数是SharedPreferences文件保存在手机中的文件名称,SharedPreferences文件保存在手机中是以.xml格式保存的,系统首先会去判断是否有这个文件,没有就回去创建;第二个参数是这个文件的访问权限,有下面几种权限:
Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。
Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写
第二步,通过SharedPreferences 获取Editor 第三步,拿到Editor之后,就可以保存数据了,主要方法有putInt(),putString()... 第四步,在通过Editor写入数据之后,一定要记得调用commit(),这样提交了数据的更改才会生效。/** * SharedPreferences参数管理类 */ public class PrefsUtils { // 本APP保存SharedPreferences参数的文件名 private static final String APP_Prefs_Name = "roadheadline"; /** * 保存字符串 * * @param context * @param key * @param value * @return 保存成功返回true */ public static boolean putString(Context context, String key, String value) { return context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE).edit().putString(key, value).commit(); } /** * 获取字符串 * * @param context * @param key * @return 若不存在该key值对应的value,返回null */ public static String getString(Context context, String key) { return getString(context, key, null); } /** * 获取字符串 * * @param context * @param key * @param defValue * @return 若不存在该key值对应的value,返回用户传入的defValue */ public static String getString(Context context, String key, String defValue) { return context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE).getString(key, defValue); } /** * 保存int数据 * * @param context * @param key * @param value * @return 保存成功返回true */ public static boolean putInt(Context context, String key, int value) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putInt(key, value); return editor.commit(); } /** * 获取int数据 * @param context * @param key * @return 不存在返回-1 */ public static int getInt(Context context, String key) { return getInt(context, key, -1); } /** * 获取int数据 * * @param context * @param key * @param defaultValue * @return */ public static int getInt(Context context, String key, int defaultValue) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); return settings.getInt(key, defaultValue); } /** * 保存long类型数据 * * @param context * @param key * @param value * @return */ public static boolean putLong(Context context, String key, long value) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putLong(key, value); return editor.commit(); } /** * 获取long类型数据 * * @param context * @param key The name of the preference to retrieve * @return 不存在返回-1 */ public static long getLong(Context context, String key) { return getLong(context, key, -1); } /** * 获取long类型数据 * * @param context * @param key * @param defaultValue * @return */ public static long getLong(Context context, String key, long defaultValue) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); return settings.getLong(key, defaultValue); } /** * 保存float类型数据 * * @param context * @param key * @param value * @return */ public static boolean putFloat(Context context, String key, float value) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putFloat(key, value); return editor.commit(); } /** * 获取float类型数据 * * @param context * @param key * @return 不存在返回-1 */ public static float getFloat(Context context, String key) { return getFloat(context, key, -1); } /** * 获取float类型数据 * * @param context * @param key * @param defaultValue * @return */ public static float getFloat(Context context, String key, float defaultValue) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); return settings.getFloat(key, defaultValue); } /** * 保存boolean类型数据 * * @param context * @param key * @param value * @return */ public static boolean putBoolean(Context context, String key, boolean value) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(key, value); return editor.commit(); } /** * 获取boolean类型数据 * * @param context * @param key * @return 不存在返回false */ public static boolean getBoolean(Context context, String key) { return getBoolean(context, key, false); } /** * 获取boolean类型数据 * * @param context * @param key * @param defaultValue * @return */ public static boolean getBoolean(Context context, String key, boolean defaultValue) { SharedPreferences settings = context.getSharedPreferences(APP_Prefs_Name, Context.MODE_PRIVATE); return settings.getBoolean(key, defaultValue); } }有了这个管理类之后,我们在需要保存数据的类里面,直接调用PrefsUtils.putString(Context context, String key, String value);或其他相应的方法即可,获取保存的数据也是调用里面相应方法即可。
文件被保存在内部存储中时,默认情况下,文件是应用程序私有的,其他应用不能访问。当用户卸载应用程序时这些文件也跟着被删除。
文件默认存储位置:/data/data/包名/files/文件名。
Android系统默认提供了两个方法openFileOutput()、openFileInput()将数据保存到内部存储器中。使用方法如下: (1)往文件中写入数据String text = "my name is lly";其中openFileOutput(String fileName,int mode),第一个参数指定要打开的文件名称(不需要写路径,因为文件默认存储位置:/data/data/包名/files/文件名),如果没有这个文件,则自动创建一个; 第二个参数指定文件访问权限,有下面四种权限:
try {
FileOutputStream fos = openFileOutput("lly.txt", Context.MODE_PRIVATE);
fos.write(text.getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
Context.MODE_PRIVATE = 0
为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容。
Context.MODE_APPEND = 32768
该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE = 1
表示当前文件可以被其他应用读取。
MODE_WORLD_WRITEABLE
表示当前文件可以被其他应用写入。
try {
FileInputStream fis = openFileInput("lly.txt");
byte[] buf = new byte[1024];
StringBuilder sb = new StringBuilder();
int len = 0;
while((len = fis.read(buf)) != -1 ){
sb.append(new String(buf,0,len));
}
fis.close();
System.out.println(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
//获取外存储的状态 String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { // 可读可写 mExternalStorageAvailable = mExternalStorageWriteable =true; } elseif ( Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // 可读 } else { // 可能有很多其他的状态,但是我们只需要知道,不能读也不能写 }
File file3 = new File(getExternalCacheDir().getAbsolutePath(), "getExternalCacheDir.txt"); try { OutputStream outputStream1 = new FileOutputStream(file3); outputStream1.write("getExternalCacheDir".getBytes()); outputStream1.close(); } catch (Exception e) { e.printStackTrace(); } Log.d("TAG", "file3=" + file3); File file4 = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "getExternalFilesDir.txt"); try { OutputStream outputStream1 = new FileOutputStream(file4); outputStream1.write("getExternalFilesDir".getBytes()); outputStream1.close(); } catch (Exception e) { e.printStackTrace(); }运行结果如下:
如果我们想缓存图片等比较耗空间的文件,推荐放在getExternalCacheDir()所在的文件下面,这个文件和getCacheDir()很像,都可以放缓存文件,在APP被卸载的时候,都会被系统删除,而且缓存的内容对其他APP是相对私有的。
在使用getExternalFilesDir(int filetype)时,我们指定了这个文件的类型,不同的类型,在路径下会放到不同的文件夹下(如上面的运行结果例子),这样,Android系统的文件扫面器在扫描文件时就可以明确知道这是什么类型文件了。如果你的APP产生的文件不需要隐藏,即对用户是可见的,那么你可以把文件放在外部的公共存储文件下面。我们可以通过下面的代码获取到公共存储目录:
Environment.getExternalStorageDirectory()
Environment.getExternalStoragePublicDirectory() 这两个方法都是Environment的,而不是Context的。第一个方法获取到的其实是外部存储的根目录,而第二个方法获取到得则是外部存储的公共目录。其实在访问权限上是没有区别的,不同点是getExternalStoragePublicDirectory()在运行的时候,会需要你带有一个特定的参数来指定这些public的文件类型,以便于与其他public文件进行分类,类型和上面的类似。public class DBOpenHelper extends SQLiteOpenHelper { private static final String DATABASENAME = "itcast.db"; //数据库名称 private static final int DATABASEVERSION = 2;//数据库版本 public DBOpenHelper(Context context) { super(context, DATABASENAME, null, DATABASEVERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE person (personid integer primary key autoincrement, name varchar(20), amount integer)");//执行有更改的sql语句 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS person"); onCreate(db); } }第二步:编写业务逻辑处理类 按照MVC模型,我们在处理数据时最好编写一个业务逻辑处理类,进行数据的增删改查操作,在这里面,当我们第一次调用getWritableDatabase()或getReadableDatabase()时,数据库会被创建。如下例子:
public class PersonService { private DBOpenHelper dbOpenHelper; public PersonService(Context context) { this.dbOpenHelper = new DBOpenHelper(context); } public void payment(){ SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.beginTransaction();//事启事务 try{ db.execSQL("update person set amount=amount-10 where personid=", new Object[]{1}); db.execSQL("update person set amount=amount+10 where personid=", new Object[]{2}); db.setTransactionSuccessful();//设置事务标志为成功,当结束事务时就会提交事务 }finally{ db.endTransaction(); } } public void save(Person person){ //如果要对数据进行更改,就调用此方法得到用于操作数据库的实例,该方法以读和写方式打开数据库 SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("insert into person (name,amount) values(,)", new Object[]{person.getName(),person.getAmount()}); } public void update(Person person){ SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("update person set name= where personid=", new Object[]{person.getName(),person.getId()}); } public void delete(Integer id){ SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("delete from person where personid=", new Object[]{id.toString()}); } public Person find(Integer id){ //如果只对数据进行读取,建议使用此方法 SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("select * from person where personid=", new String[]{id.toString()}); if(cursor.moveToFirst()){ int personid = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); int amount = cursor.getInt(cursor.getColumnIndex("amount")); Person person = new Person(personid, name); person.setAmount(amount); return person; } return null; } public List<Person> getScrollData(Integer offset, Integer maxResult){ List<Person> persons = new ArrayList<Person>(); SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("select * from person limit ,", new String[]{offset.toString(), maxResult.toString()}); while(cursor.moveToNext()){ int personid = cursor.getInt(cursor.getColumnIndex("personid")); String name = cursor.getString(cursor.getColumnIndex("name")); int amount = cursor.getInt(cursor.getColumnIndex("amount")); Person person = new Person(personid, name); person.setAmount(amount); persons.add(person); } cursor.close(); return persons; } public Cursor getCursorScrollData(Integer offset, Integer maxResult){ SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); return db.rawQuery("select personid as _id, name, amount from person limit ,", new String[]{offset.toString(), maxResult.toString()}); } public long getCount() { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("select count(*) from person", null); cursor.moveToFirst(); return cursor.getLong(0); } }可以看到,和我们平常使用的SQL非常类似。
PersonService personService = new PersonService(this.getContext()); Person person = new Person(); person.setName("xiaoxiao"); person.setAmount(100); personService.save(person);
public class PersonProvider extends ContentProvider { private DBOpenHelper dbOpenHelper; private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH); private static final int PERSONS = 1; private static final int PERSON = 2; //对外公布两种对共享数据的操作方式:集合操作方式(批量方式)、单条记录操作方式 static{ MATCHER.addURI("cn.itcast.providers.personprovider", "person", PERSONS);//以集合方式进行操作(批量操作) MATCHER.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);//单条记录操作 } @Override public boolean onCreate() { this.dbOpenHelper = new DBOpenHelper(this.getContext()); return false; } @Override public String getType(Uri uri) {//返回当前操作的数据的mimeType switch (MATCHER.match(uri)) { case PERSONS: return "vnd.android.cursor.dir/person";//对集合的操作 case PERSON: return "vnd.android.cursor.item/person";//对单条记录的操作 default: throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString()); } } //删除person表中的所有记录 /person //删除person表中指定id的记录 /person/10 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int count = 0; switch (MATCHER.match(uri)) { case PERSONS: count = db.delete("person", selection, selectionArgs); return count; case PERSON: long id = ContentUris.parseId(uri); String where = "personid="+ id; if(selection!=null && !"".equals(selection)){ where = selection + " and " + where; } count = db.delete("person", where, selectionArgs); return count; default: throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString()); } } @Override public Uri insert(Uri uri, ContentValues values) {// /person SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: long rowid = db.insert("person", "name", values); Uri insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增记录的Uri this.getContext().getContentResolver().notifyChange(uri, null); return insertUri; default: throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString()); } } //查询person表中的所有记录 /person //查询person表中指定id的记录 /person/10 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); switch (MATCHER.match(uri)) { case PERSONS: return db.query("person", projection, selection, selectionArgs, null, null, sortOrder); case PERSON: long id = ContentUris.parseId(uri); String where = "personid="+ id; if(selection!=null && !"".equals(selection)){ where = selection + " and " + where; } return db.query("person", projection, where, selectionArgs, null, null, sortOrder); default: throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString()); } } //更新person表中的所有记录 /person //更新person表中指定id的记录 /person/10 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); int count = 0; switch (MATCHER.match(uri)) { case PERSONS: count = db.update("person", values, selection, selectionArgs); return count; case PERSON: long id = ContentUris.parseId(uri); String where = "personid="+ id; if(selection!=null && !"".equals(selection)){ where = selection + " and " + where; } count = db.update("person", values, where, selectionArgs); return count; default: throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString()); } } }可以看到,一个ContentProvider主要有三部分: (1)onCreate()数据初始化 在这里面进行数据的初始化,比如创建出数据库... (2)对外提供操作共享数据的路径机制 一个APP想要对另外一个APP进行共享数据的操作功能,首先要能够拿到访问操作方法的URI,类似于我们访问WEN服务器的接口地址一样。Android系统内部对共享数据的操作模式有集合形式的操作(批量操作)、单条记录的操作模式,分别用“vnd.android.cursor.dir/自定义”和“vnd.android.cursor.item/自定义”来区分。在对共享数据进行操作之前,ContentProvider首先会判断是哪种操作模式。因此,我们自定义的eContentProvider里面需要进行如下工作: > 首先,在我们实现的ContentProvider子类中,对外提供这个共享数据能够被操作的方式,如: //对外公布两种对共享数据的操作方式:集合操作方式(批量方式)、单条记录操作方式 static{ MATCHER.addURI("cn.itcast.providers.personprovider", "person", PERSONS);//以集合方式进行操作(批量操作) MATCHER.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);//单条记录操作 } > 在使用ContentProvider进行数据操作前,默认会进行getType(Uri uri)方法,在这里面,拿到外部APP传进来的Uri进行匹配,判断出操作类别。 (3)调用相应的操作方法 在执行完第二步后,根据第三方的请求调用的方法,调用ContentProvider中相应的方法。
热门源码