七、运行时权限
低于6.0系统只需要在AndroidManifest.xml加入需要的权限,用户同意安装时会统一授予。6.0之后加入运行时权限,即在使用过程中授予权限,可以随时回收权限。
普通权限:申请时系统自动进行授权。
危险权限:设计隐私和设备安全需要手动授权。9组24个。
权限列表:http://developer.android.google.cn/reference/android/Manifest.permission.html
权限组 | 权限 |
---|---|
CALENDAR |
|
CAMERA |
|
CONTACTS |
|
LOCATION |
|
MICROPHONE |
|
PHONE |
|
SENSORS |
|
SMS |
|
STORAGE |
1、在AndroidManifest.xml中声明
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.runtimepermissiontest"> <uses-permission android:name="android.permission.CALL_PHONE" /> <application> ...... </application> </manifest>
2、在代码中申请
处理授予和不授予的执行代码
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { ...... makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(ContextCompat.checkSelfPermission(ManiActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(ManiActivity.this, new String[] {Manifest.permission.CALL_PHONE}, 1) } }); } private void call() { try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); } catch (SecurityException e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { call(); } else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } } }
八、ContentProvider
APP通过ContentProvider对其数据向外提供访问接口。想要对该数据执行CRUD操作首先要通过Context中的getContentResolver()得到ContentResolver类。通过该类调用insert()等方法。这些方法都要接收一个Uri参数指定内容。
content://author+path
Uri mUri = Uri.parse("content://com.example.app.provider/table1");
1、AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.databasetest"> <application .......> ....... </activity> <provider android:name=".DatabaseProvider" android:authorities="com.example.databasetest.provider" android:enabled="true" android:exported="true" /> </application> </manifest>
2、MyDatabaseHelper
创建数据库
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "pages integer, " + "name text)"; public static final String CREATE_CATEGORY = "create table Category (" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; private Context mContext; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); // Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table if exists Book"); db.execSQL("drop table if exists Category"); onCreate(db); } }
3、DataBaseProvider.java
创建ContentProvider,需要重写各种操作方法, getType返回Uri对象的MIME类型。
目录:vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category
文件:vnd.android.cursor.item/vnd.com.example.databasetest. provider.category
public class DatabaseProvider extends ContentProvider { public static final int BOOK_DIR = 0; public static final int BOOK_ITEM = 1; public static final int CATEGORY_DIR = 2; public static final int CATEGORY_ITEM = 3; public static final String AUTHORITY = "com.example.databasetest.provider"; private static UriMatcher uriMatcher; private MyDatabaseHelper dbHelper; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR); uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);/#匹配任意长度数字 uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR); uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM); } @Override public boolean onCreate() { dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // 查询数据 SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = null; switch (uriMatcher.match(uri)) { case BOOK_DIR: cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder); break; case BOOK_ITEM: String bookId = uri.getPathSegments().get(1); cursor = db.query("Book", projection, "id = ?", new String[] { bookId }, null, null, sortOrder); break; case CATEGORY_DIR: cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder); break; case CATEGORY_ITEM: String categoryId = uri.getPathSegments().get(1); cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder); break; default: break; } return cursor; } @Override public Uri insert(Uri uri, ContentValues values) { // 添加数据 SQLiteDatabase db = dbHelper.getWritableDatabase(); Uri uriReturn = null; switch (uriMatcher.match(uri)) { case BOOK_DIR: case BOOK_ITEM: long newBookId = db.insert("Book", null, values); uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId); break; case CATEGORY_DIR: case CATEGORY_ITEM: long newCategoryId = db.insert("Category", null, values); uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId); break; default: break; } return uriReturn; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // 更新数据 SQLiteDatabase db = dbHelper.getWritableDatabase(); int updatedRows = 0; switch (uriMatcher.match(uri)) { case BOOK_DIR: updatedRows = db.update("Book", values, selection, selectionArgs); break; case BOOK_ITEM: String bookId = uri.getPathSegments().get(1); updatedRows = db.update("Book", values, "id = ?", new String[] { bookId }); break; case CATEGORY_DIR: updatedRows = db.update("Category", values, selection, selectionArgs); break; case CATEGORY_ITEM: String categoryId = uri.getPathSegments().get(1); updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId }); break; default: break; } return updatedRows; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // 删除数据 SQLiteDatabase db = dbHelper.getWritableDatabase(); int deletedRows = 0; switch (uriMatcher.match(uri)) { case BOOK_DIR: deletedRows = db.delete("Book", selection, selectionArgs); break; case BOOK_ITEM: String bookId = uri.getPathSegments().get(1); deletedRows = db.delete("Book", "id = ?", new String[] { bookId }); break; case CATEGORY_DIR: deletedRows = db.delete("Category", selection, selectionArgs); break; case CATEGORY_ITEM: String categoryId = uri.getPathSegments().get(1); deletedRows = db.delete("Category", "id = ?", new String[] { categoryId }); break; default: break; } return deletedRows; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case BOOK_DIR: return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book"; case BOOK_ITEM: return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book"; case CATEGORY_DIR: return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category"; case CATEGORY_ITEM: return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category"; } return null; } }
4、ManiActivity操作ContentProvider
public class MainActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); Button createDatabase = (Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dbHelper.getWritableDatabase(); } }); Button addData = (Button) findViewById(R.id.add_data); addData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); // 开始组装第一条数据 values.put("name", "The Da Vinci Code"); values.put("author", "Dan Brown"); values.put("pages", 454); values.put("price", 16.96); db.insert("Book", null, values); // 插入第一条数据 values.clear(); // 开始组装第二条数据 values.put("name", "The Lost Symbol"); values.put("author", "Dan Brown"); values.put("pages", 510); values.put("price", 19.95); db.insert("Book", null, values); // 插入第二条数据 } }); Button updateData = (Button) findViewById(R.id.update_data); updateData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price", 10.99); db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" }); } }); Button deleteButton = (Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "pages > ?", new String[] { "500" }); } }); Button queryButton = (Button) findViewById(R.id.query_data); queryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); // 查询Book表中所有的数据 Cursor cursor = db.query("Book", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { String name = cursor.getString(cursor.getColumnIndex("name")); String author = cursor.getString(cursor.getColumnIndex("author")); int pages = cursor.getInt(cursor.getColumnIndex("pages")); double price = cursor.getDouble(cursor.getColumnIndex("price")); } while (cursor.moveToNext()); } cursor.close(); } }); } }
九、Service
Service实现APP后台运行,适合不与用户交互但需长期运行的任务。服务不运行在单独的进程,依赖于创建服务时所在应用程序进程。服务默认不开启线程,即它默认运行在主线程需要手动在内部创建线程。
1、多线程操作
#继承方式 class MyThread extend Thread { public void run() { } } //启动 new MyThread().start(); #接口方式 class MyThread implements Runnable { public void run() { } } //启动 MyThread mThread = new MyThread(); new Thread(mThread).start(); #匿名类 new Thread(new Runnable() { public void run() { } }).start();
2、UI操作
Android中的异步消息主要由4部分组成:Message, Handler, MessageQueue和Looper。MessageQueue用于存放通过Handler发送的消息,这部分消息已知存在于消息队列等待被处理。每个线程只有一个MessageQueue对象。Looper是每个线程MessageQueue的管家,调用Looper的loop()方法后,就会将它取出并传递到Handler的handlerMessage()方法中。每个线程也只有一个Looper对象。
一般首先在主线程中创建一个Handler对象,并重写HandlerMessage()方法。当子线程需要进程UI操作时,创建一个Message对象并通过Handler发出这条消息。之后这条消息会被添加到MessageQueue中等待被处理。而Looper则一直尝试从MessageQueue中取出待处理消息,分发到handler中的handleMessage()方法中。
public class MainActivity extends AppCompatActivity implements View.OnClickListener { public static final int UPDATE_TEXT = 1; private TextView text; private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: // 在这里可以进行UI操作 text.setText("Nice to meet you"); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView) findViewById(R.id.text); Button changeText = (Button) findViewById(R.id.change_text); changeText.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.change_text: new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); // 将Message对象发送出去 } }).start(); break; default: break; } } }
3、Service基本用法
onCreate()方法是在服务第一次创建时候调用,而onStartCommand()方法则是每次启动服务都会调用。onBind()方法在Activity绑定服务时回调。
MainService.java
public class MyService extends Service { public MyService() { } private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { public void startDownload() { Log.d("MyService", "startDownload executed"); } public int getProgress() { Log.d("MyService", "getProgress executed"); return 0; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); Log.d("MyService", "onCreate executed"); Intent intent = new Intent(this, MainActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); Notification notification = new NotificationCompat.Builder(this) .setContentTitle("This is content title") .setContentText("This is content text") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .setContentIntent(pi) .build(); startForeground(1, notification);//启动前台服务 } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d("MyService", "onStartCommand executed"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.d("MyService", "onDestroy executed"); } }
MainActivity
重写ServiceConnection的onServiceConnection()和onServiceDisconnected(),他们在活动和服务绑定和解绑时调用。每调用一个startService(),onStartCommand()就会执行一次,但实际上每个服务只存在一个实例。所以不管调用了多少次startService(), 只需要调用一次stopService()或stopSelf()。调用bindService()来获得一个和服务的持久连接。这时会回调onBind()方法。如果该服务还未创建则先调用onCreate()。
如果对一个服务既调用了startService()又调用了bindService(),则必须同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private MyService.DownloadBinder downloadBinder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { downloadBinder = (MyService.DownloadBinder) service; downloadBinder.startDownload(); downloadBinder.getProgress(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startService = (Button) findViewById(R.id.start_service); Button stopService = (Button) findViewById(R.id.stop_service); startService.setOnClickListener(this); stopService.setOnClickListener(this); Button bindService = (Button) findViewById(R.id.bind_service); Button unbindService = (Button) findViewById(R.id.unbind_service); bindService.setOnClickListener(this); unbindService.setOnClickListener(this); Button startIntentService = (Button) findViewById(R.id.start_intent_service); startIntentService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.start_service: Intent startIntent = new Intent(this, MyService.class); startService(startIntent); // 启动服务 break; case R.id.stop_service: Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent); // 停止服务 break; case R.id.bind_service: Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务 break; case R.id.unbind_service: unbindService(connection); // 解绑服务 break; case R.id.start_intent_service: // 打印主线程的id Log.d("MainActivity", "Thread id is " + Thread.currentThread(). getId()); Intent intentService = new Intent(this, MyIntentService.class); startService(intentService); break; default: break; } } }
IntentService
服务默认执行在主线程中,如果需要处理耗时操作需要在Service中手动开启和关闭子线程。Android设计了IntentService类,启动后自动执行其中的onHandleIntent()方法,执行完执行onDestroy()方法。
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); // 调用父类的有参构造函数 } @Override protected void onHandleIntent(Intent intent) { // 打印当前线程的id Log.d("MyIntentService", "Thread id is " + Thread.currentThread(). getId()); } @Override public void onDestroy() { super.onDestroy(); Log.d("MyIntentService", "onDestroy executed"); } }
10、Application
Android提供一个Application类,每当程序启动时,系统自动初始化这个类。我们可以自己定制这个类,以便获取全局信息,如Context。 程序运行时自动执行onCreate(), 在Activity中只需要执行 MyApplication.getContext()
public class MyApplication extends Application { private static Context context; @override public void onCreate() { context = getApplicationCcontext(); } public static Context getContext() { return context; } }
还需要在AndroidManifest.xml中声明这个Application
<application android:name="com.example.howie.MyApplication" ...> </application>