Android第一行代码阅读笔记
1、Android系统简介
1.1、Android系统架构
Linux内核层 :Android 系统基于Linux内核,这一层为Android 设备的各种硬件提供了底层的驱动,如显示驱动、音频驱动、相机驱动、蓝牙驱动、WiFi驱动、电源管理。
系统运行库层 :这一层通过一些C/C++库来为Android系统提供主要的特性支持,如SQLite提供数据库的支持,OpenGL|ES库提供3D绘图的支持,Webkit库提供浏览器内核的支持。以及一些核心库允许开发者构建Android应用。
应用框架层 :这一层提供了构建应用程序可能用到的各种API。
应用层 :所有安装在手机上的应用程序都是属于这一层的。
1.2、Android系统四大组件
活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供者(Content Provider)
2、活动
活动是一种包含用户界面的组件,用于和用户交互。
2.1、创建一个Activity
1、新建一个empty activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class FirstActivity extends AppCompatActivity { @SuppressLint("NonConstantResourceId") @Override public boolean onOptionsItemSelected (@NonNull MenuItem item) { switch (item.getItemId()){ case R.id.add_item: Toast.makeText(this ,"you clicked add button" ,Toast.LENGTH_SHORT).show(); break ; case R.id.remove_item: Toast.makeText(this ,"you removed the button" ,Toast.LENGTH_SHORT).show(); break ; default : } return true ; } @Override public boolean onCreateOptionsMenu (Menu menu) { getMenuInflater().inflate(R.menu.main,menu); return true ; } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.first_layout); Button button1=(Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View view) { startActivity(intent2); } }); } }
2、布局文件
Android程序设计讲究逻辑和视图分离,最好一个活动对应一个布局。下面手动创建一个Layout resource file,根元素默认为LinearLayout。
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:orientation ="vertical" android:layout_width ="match_parent" //宽度适配父元素 android:layout_height ="match_parent" > <Button android:id ="@+id/button1" //定义一个id 名为button1 android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="确认" /> </LinearLayout >
创建一个菜单布局
1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android ="http://schemas.android.com/apk/res/android" > <item android:id ="@+id/add_item" android:title ="add" /> <item android:id ="@+id/remove_item" android:title ="remove" /> </menu >
3、在AndroidManifest文件中注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.example.activitytest" > <application android:allowBackup ="true" android:icon ="@mipmap/ic_launcher" android:label ="@string/app_name" android:roundIcon ="@mipmap/ic_launcher_round" android:supportsRtl ="true" android:theme ="@style/Theme.ActivityTest" > <activity android:name =".ThirdActivity" > <intent-filter tools:ignore ="AppLinkUrlError" > <action android:name ="android.intent.action.VIEW" /> <category android:name ="android.intent.category.DEFAULT" /> <data android:scheme ="http" /> </intent-filter > </activity > <activity android:name =".SecondActivity" > <intent-filter > <action android:name ="com.example.activitytest.My_Category" /> <category android:name ="android.intent.category.DEFAULT" /> <category android:name ="com.example.activitytest.My_Category1" /> </intent-filter > </activity > <activity android:name =".FirstActivity" > <intent-filter > <action android:name ="android.intent.action.MAIN" /> <category android:name ="android.intent.category.LAUNCHER" /> </intent-filter > </activity > </application > </manifest >
2.2、活动间数据传递
1、Intent
Intent用于在Android各组件之间进行交互,不仅可以指明当前组件需要执行的动作,还可以在不同组件之间传递数据。具体如:
1.1、Intent类型:
显式Intent :可以通过类名找到相应的组件。
1 2 Intent intent = new Intent(context,XXActivity.class); startActivity(intent);
隐式Intent :不指定具体的组件,但是会声明将要执行的操作。
1 2 3 4 Intent intent = new Intent(Intent.ACTION_DIAL); Uri data = Uri.parse("tel:" + "135xxxxxxxx" ); intent.setData(data); startActivity(intent);
1.2、PendingIntent
PendingIntent可以看作是对Intent的一个封装,但它不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为(启动特定Service,Activity,BroadcastReceiver)。我们可以把Pending Intent交给其他程序,其他程序按照PendingIntent进行操作
使用场景:
当用户点击通知栏的时候,才执行的Intent(系统的NotificationManager执行的Intent)。
当用户操作悬浮在主屏幕中的小工具,才执行的Intent。
在未来某一个特定时间执行的Intent(系统的AlarmManager执行的Intent)
PengdingIntent实例化: PengdingIntent.getActivity()
、PengdingIntent.getService()
、pengdingIntent.getBroadcast()
。
Intent使用简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String data="hello ! secondActivity" ; Intent intent3=new Intent(FirstActivity.this ,SecondActivity.class); intent3.putExtra("extra_data" ,data); Intent intent=getIntent(); String data=intent.getStringExtra("extra_data" ); Log.d("SecondActivity" ,data);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Intent intent=new Intent(FirstActivity.this ,SecondActivity.class); startActivityForResult(intent,1 ); @override protected void onActivityResult (int requestCode,int resultCode,Intent data) { switch (requestCode){ case 1 : if (resultCode==RESULT_OK){ String returnedData=data.getStringExtra("data_return" ); Log.d("FirstActivity" ,returnedData); } break ; default : } } public void onClick (View v) { Intent intent=new Intent(); intent.putExtra("data_return" ,"hello,FirstActivity" ); setResult(RESULT_OK,intent); finish(); }
2.3、活动的生命周期
**返回栈:**一个任务就是一组存放在栈里的活动集合,这个栈称为返回栈。
活动状态:运行、暂停、停止、销毁
onCreate():
在活动第一次被创建的时候调用,完成活动的初始化操作。如加载布局、绑定事件。
onStart():
在活动由不可见变为可见的时候调用。
onResume():
在活动准备好和用户进行交互时调用。此时的活动处于返回栈的栈顶,并且处于运行状态。
onPause():
在系统准备去启动或恢复另一个活动的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据。
onStop():
在活动完全不可见的时候调用。和onPause()
方法的区别在于如果启动的是一个对话框式的活动,那么onPause()
方法会得到执行,而onStop()
方法并不会被执行。
onDestroy():
在活动被销毁之前调用,之后活动的状态将变为销毁状态。
onRestart():
在活动由停止状态变为运行状态之前调用,用以重启活动。
**总结:**以上方法除了重启,具有两两相对的关系。具体如下:
完整生存期 :onCreate()
和onDestroy()
方法之间经历的就是完整生存期。一个活动会在onCreate()
方法中完成各种初始化操作,而在onDestroy()
中完成释放内存的操作。
可见生存期 :onStart()
和onStop()
方法之间经历的就是可见生存期。在可见生存期内,活动对于用户是可见的,即便有可能无法与用户进行交互。一般在onStart()方法中对资源进行加载,在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
前台生存期 :onResume()
和onPause()
方法之间经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动可以和用户进行交互。
2.4、活动的启动模式
standard :活动默认的启动模式。在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置,对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
singleTop :当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
singleTask :使用singleTop模式如果活动没有处于栈顶,还是可能会创建多个活动实例,那有没有什么办法可以让某个活动在整个应用程序的上下文只存在一个实例呢?当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已存在则使用该实例,并把在这个活动之上的所有活动出栈,如果没有就会创建一个新的活动。
singleInstance :指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。假设程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,便可以使用singleInstance模式。在该模式下,会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈,也就解决了共享活动实例的问题。
3、广播机制
Android应用可以通过广播从系统或其他APP接收或发送消息,类似发布订阅模式。系统在某些状态改变如开机、充电时会发出广播,应用也可以自定义广播。
广播类型:标准广播、有序广播
标准广播 :一种完全异步执行的广播,在广播发出后,所有的广播接收器几乎都会在同一时刻收到这条广播消息,接收者之间无先后顺序而言。效率较高,同时无法被截断。
有序广播 :一种同步执行的广播。在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器的逻辑执行完毕后广播才会继续传递,有先后顺序,优先级高的先收到广播信息。并且前面的广播接收器还可以截断正在传递的广播。
本地广播 :只在本APP发送和接收的广播。注册为本地广播的接收器无法收到标准广播。
注册广播的方式:
静态注册:在AndroidManifest.xml中注册 。
动态注册:在代码中注册,动态注册的广播需要在onDestroy()
方法中调用unregisterReceiver()
取消注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { Toast.makeText(context,"network changes" ,Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy () { super .onDestroy(); unregisterReceiver(networkChangeReceiver); } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE" ); networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver,intentFilter); } } <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { Toast.makeText(context,"Boot Complete!" ,Toast.LENGTH_SHORT).show(); } } <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
本地广播 :发出的广播只能在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。以解决安全性问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; class LocalReceiver extends BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { Toast.makeText(context,"receive local broadcast" ,Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy () { super .onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager=LocalBroadcastManager.getInstance(this ); Button button=(Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent=new Intent("com.example.broadcasttest.LOCAL_BROADCAST" ); localBroadcastManager.sendBroadcast(intent); } }); intentFilter=new IntentFilter(); intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST" ); localReceiver=new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver,intentFilter); } }
1 2 3 4 Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST" ); sendBroadcast(intent); sendOederedBroadcast(intent,null ); localBroadcastManager.sendBroadcast(intent);
4、数据存储
4.1、Java IO回顾
文件流:直接操作文件的流,分为字节流(FileInputStream和FileOutputStream)和字符流(FileReader和FileWriter)。其中字节流可用于操作一切文件,而字符流只能用于操作文本文件。
程序从输入流读取数据,向输出流写入数据。
FileInputStream :从文件读取数据。
1 InputStream f=new FileInputStream("F:/java/hello.txt" );
FileOutputStream :创建一个文件并向文件中写数据。
BufferedReader/BufferedWriter :处理流,将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。
BufferedInputStream/BufferedOutputStream :处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高 读写效率。
4.2、文件存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class MainActivity extends AppCompatActivity { private EditText editText; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText=(EditText) findViewById(R.id.edit); String inputText=load(); if (!TextUtils.isEmpty(inputText)){ editText.setText(inputText); editText.setSelection(inputText.length()); Toast.makeText(this ,"restore success" ,Toast.LENGTH_SHORT).show(); } } private String load () { FileInputStream in=null ; BufferedReader reader=null ; StringBuilder content=new StringBuilder(); try { in=openFileInput("data" ); reader=new BufferedReader(new InputStreamReader(in)); String line="" ; while ((line=reader.readLine())!=null ){ content.append(line); } }catch (IOException e){ e.printStackTrace(); }finally { if (reader!=null ){ try { reader.close(); }catch (IOException e){ e.printStackTrace(); } } } return content.toString(); } @Override protected void onDestroy () { super .onDestroy(); String inputText=editText.getText().toString(); save(inputText); } public void save (String inputText) { FileOutputStream out=null ; BufferedWriter writer=null ; try { out=openFileOutput("data" , Context.MODE_PRIVATE); writer=new BufferedWriter(new OutputStreamWriter(out)); writer.write(inputText); }catch (IOException e){ e.printStackTrace(); }finally { try { if (writer!=null ){ writer.close(); } }catch (IOException e){ e.printStackTrace(); } } } }
4.3、SharedPreferences存储
获取SharedPreferences对象的方式:(1)Context类中的getSharedPreferences()
方法。(2)Activity类中的getPreferences()
方法。PreferenceManager类中的getDefaultSharedPreferences()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button saveData=(Button) findViewById(R.id.save_data); saveData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { SharedPreferences.Editor editor=getSharedPreferences("data" ,MODE_PRIVATE).edit(); editor.putBoolean("married" ,false ); editor.putInt("age" ,28 ); editor.putString("name" ,"Tom" ); editor.apply(); } }); Button restoreData=(Button) findViewById(R.id.restore_data); restoreData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { SharedPreferences pref=getSharedPreferences("data" ,MODE_PRIVATE); String name=pref.getString("name" ,"" ); int age=pref.getInt("age" ,0 ); boolean married=pref.getBoolean("married" ,false ); Log.d("MainActivity" ,"name is" +name); Log.d("MainActivity" ,"age is" +age); Log.d("MainActivity" ,"married is" +married); } }); } }
4.4、SQLite数据库存储
SQLite是一款轻量级的关系型数据库,运算速度快,占用资源少,支持SQL语法以及数据库的ACID特性,用于存储大量复杂的关系型数据。
1、创建数据库
SQLiteOpenHelper帮助类实现对数据库进行创建和升级。
SQLiteOpenHelper是一个抽象类,提供onCreate()
和onUpgrade()
两个抽象方法,在使用时需要创建一个自己的帮助类继承它。
SQLiteOpenHelper中还有两个非常重要的实例方法:getReadableDatabase()
和getWriteableDatabase()
。用于创建或打开一个现有的数据库(如无则创建),并返回一个可对数据库进行读写操作的对象。当磁盘已满不可写入时,getReadableDatabase()
方法返回的对象将以只读的方式去打开数据库,而getWriteableDatabase()
将抛出异常。
SQLiteOpenHelper的构造方法接收4个参数,分别是Context、数据库名、Cursor(返回查询数据)、当前数据库版本号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 (@Nullable Context context, @Nullable String name, @Nullable 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 success" ,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); } }
2、增删改查(CURD)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 public class MainActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); 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" ,"细雨湿流光" ); values.put("author" ,"jun" ); values.put("price" ,34 ); values.put("pages" ,344 ); db.insert("Book" ,null ,values); values.clear(); } }); 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("name" ,"时光笔迹" ); db.update("Book" ,values,"author=?" ,new String[]{"jun" }); } }); Button deleteData=(Button) findViewById(R.id.delete_data); deleteData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { SQLiteDatabase db=dbHelper.getWritableDatabase(); db.delete("Book" ,"pages>?" ,new String[]{"400" }); } }); Button queryData=(Button) findViewById(R.id.query_data); queryData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { SQLiteDatabase db=dbHelper.getWritableDatabase(); 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" )); Log.d("MainActivity" ,name); Log.d("MainActivity" ,author); Log.d("MainActivity" , String.valueOf(pages)); Log.d("MainActivity" , String.valueOf(price)); }while (cursor.moveToNext()); } cursor.close(); } }); } }
3、使用SQL操作数据库
1 2 3 4 5 6 7 8 db.execSQL("insert into Book (name,author,pages,price) values(?,?,?,?)" ,new String[]{"xxx" ,"xxx" ,"xxx" ,"xxx" }); db.execSQL("update Book set price=? where name=?" ,new String[]{"10" ,"xxx" }); db.execSQL("delete from Book where pages>?" ,new String[]{"500" }); db.rawQuery("select * from Book" ,null );
4.5、使用LitePal操作数据库
1、配置LitePal
1、app/build.gradle,在dependencies中添加
1 implementation 'org.litepal.android:core:1.3.2'
2、app/src/main 目录下新建assets目录,并在该目录新建litepal.xml 文件
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="utf-8" ?> <litepal > <dbname value ="BookStore" > </dbname > <version value ='1' > </version > <list > <mapping class ="com.example.litepalapplication.Book" > </mapping > </list > </litepal >
3、修改AndroidManifest.xml 文件,application标签下添加
1 2 3 <application android:name ="org.litepal.LitePalApplication" </application >
2、创建JavaBean
1 2 3 4 5 6 7 8 9 public class Book extends DataSupport { private int id; private String author; private double price; private int pages; private String name; }
3、增删改查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button createDatabase=(Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Connector.getDatabase(); } }); Button addData=(Button) findViewById(R.id.add_data); addData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Book book=new Book(); book.setName("xxx" ); book.setAuthor("xxx" ); book.setPages(22 ); book.setPrice(56 ); book.save(); } }); Button updateData=(Button) findViewById(R.id.update_data); updateData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Book book=new Book(); book.setPrice(23 ); book.updateAll("name=? and author=? " ,"xxx" ,"xxx" ); } }); Button deleteData=(Button) findViewById(R.id.delete_data); deleteData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { DataSupport.deleteAll(Book.class,"price<?" ,"15" ); } }); Button queryData=(Button) findViewById(R.id.query_data); queryData.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { List<Book> books=DataSupport.findAll(Book.class); for (Book book:books){ Log.d("MainActivity" ,book.getName()); } Book firstBook=DataSupport.findFirst(Book.class); Book lastBook=DataSupport.findLast(Book.class); List<Book> books1=DataSupport.select("name" ,"author" ).find(Book.class); List<Book> books2=DataSupport.where("pages>?" ,"400" ).find(Book.class); List<Book> books3=DataSupport.order("price desc" ).find(Book.class); List<Book> books4=DataSupport.limit(3 ).find(Book.class); List<Book> books5=DataSupport.limit(3 ).offset(1 ).find(Book.class); List<Book> books6=DataSupport.select("name" ,"author" ,"pages" ).where("pages>?" ,"50" ).order("pages" ).limit(10 ).offset(1 ).find(Book.class); } }); } }
5、内容提供器
内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。
5.1、运行时权限
普通权限 :不会直接威胁到用户安全和隐私的权限,对于这部分权限申请,系统会自动帮我们授权,而不需要用户再去手动操作。
危险权限 :会触及用户隐私和安全,如获取联系人信息、定位设备的物理位置。需要用户手动授权。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button makeCall=(Button) findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this ,new String[]{Manifest.permission.CALL_PHONE},1 ); }else { call(); } } }); } 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, @NonNull String[] permissions, @NonNull int [] grantResults) { super .onRequestPermissionsResult(requestCode, permissions, 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 : } } }
5.2、访问其他程序中的数据
如果一个应用程序通过内容提供器对其数据提供了外部访问接口,那么其他应用程序都可以对这部分数据进行访问。Android系统自带的电话簿、短信、媒体库都提供了这样的访问接口供第三方应用访问数据资源。
ContentResolver
应用程序在访问内容提供器中共享的数据时需要借助ContentResolver类,可以通过Context中的getContentResolver()
方法获取该类的实例。并且,ContentResolver提供了一系列方法用以对数据进行增删改查操作,在CRUD方法中接收内容URI参数。
URI字符串 :由authority和path组成,示例:content://com.example.app.provider/table1。
在得到URI字符串后,需要将其解析为Uri对象才可作为参数传入。
1 Uri uri=Uri.parse("content://com.example.app.provider/table1" );
查询table1中的数据:
1 2 3 4 5 6 7 Cursor cursor=getContentResolver().query(uri,projection,selection, selectionArgs,sortOrder);
从Cursor中取出数据:
1 2 3 4 5 6 7 if (cursor!=null ){ while (cursor.moveToNext()){ String column1=cursor.getString(cursor.getColumnIndex("column1" )); int column2=cursor.getInt(cursor.getColumnIndex("column2" )); } cursor.close(); }
添加数据:
1 2 3 4 ContentValues values=new ContentValues(); values.put("column1" ,"text" ); values.put("column2" ,1 ); getContentResolver().insert(uri,values);
更新数据:
1 2 3 ContentValues values=new ContentValues(); values.put("column1" ,"" ); getContentResolver().update(uri,values,"column1=? and column2=?" ,new String[]{"text" ,"1" });
删除数据:
1 getContentResolver().delete(uri,"column2=?" ,new String[]{"1" });
简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class MainActivity extends AppCompatActivity { ArrayAdapter<String> adapter; List<String> contactList=new ArrayList<>(); @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView contactsView=(ListView) findViewById(R.id.contacts_view); adapter=new ArrayAdapter<String>(this , android.R.layout.simple_list_item_1,contactList); contactsView.setAdapter(adapter); if (ContextCompat.checkSelfPermission(this , Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this ,new String[]{Manifest.permission.READ_CONTACTS},1 ); }else { readContacts(); } } protected void readContacts () { Cursor cursor=null ; try { cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null ,null ,null ,null ); if (cursor!=null ){ while (cursor.moveToNext()){ String displayName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); contactList.add(displayName+"\n" +number); } adapter.notifyDataSetChanged(); } }catch (Exception e){ e.printStackTrace(); }finally { if (cursor!=null ){ cursor.close(); } } } @Override public void onRequestPermissionsResult (int requestCode, @NonNull String[] permissions, @NonNull int [] grantResults) { super .onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 1 : if (grantResults.length>0 && grantResults[0 ]==PackageManager.PERMISSION_GRANTED){ readContacts(); }else { Toast.makeText(this ,"you denied the permission" ,Toast.LENGTH_SHORT).show(); } break ; default : } } }
6、多媒体
6.1、使用通知
1 implementation "com.android.support:support-compat:28.0.0"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class MainActivity extends AppCompatActivity implements View .OnClickListener { private NotificationManager manager; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "chat" ; String channelName = "聊天消息" ; int importance = NotificationManager.IMPORTANCE_HIGH; createNotificationChannel(channelId, channelName, importance); } Button chat = findViewById(R.id.chat); chat.setOnClickListener(this ); } @TargetApi(Build.VERSION_CODES.O) private void createNotificationChannel (String channelId, String channelName, int importance) { NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); NotificationManager notificationManager = (NotificationManager) getSystemService( NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); } @Override public void onClick (View view) { switch (view.getId()){ case R.id.chat: Intent intent=new Intent(MainActivity.this ,OtherActivity.class); PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this ,0 ,intent,0 ); Notification notification = new NotificationCompat.Builder(this , "chat" ) .setAutoCancel(true ) .setContentTitle("长相思" ) .setContentText("汴水流,泗水流,流到瓜州古渡头,吴山点点愁" ) .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setColor(Color.parseColor("#F00606" )) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .setContentIntent(pendingIntent) .build(); manager.notify(1 , notification); break ; } } }
7、服务
服务是Android中实现程序后台运行的解决方案,适合去执行那些不需要和用户交互而且还要求长期运行的任务。
服务并不是运行在一个独立的进程当中,而是依赖创建服务时所在的应用程序进程。当某个应用出现进程被杀掉时,所有依赖于该进程的服务也会停止运行。
7.1、Android多线程编程
Android不允许在子线程中进行UI操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class MainActivity extends AppCompatActivity implements View .OnClickListener { private TextView textView; public static final int UPDATE_TEXT=1 ; private Handler handler=new Handler(){ public void handleMessage (Message msg) { switch (msg.what){ case UPDATE_TEXT: textView.setText("Nice to meet you" ); break ; default : break ; } } }; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView=(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); } }).start(); break ; default : break ; } } }
异步消息处理机制:
Message :Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
Handler :用于发送和处理消息。发送消息使用sendMessage()
方法,处理消息handleMessage()
。
MessageQueue :消息队列,用于存放所有通过Handler发送的消息。每个线程只有一个MessageQueue对象。
Looper :Looper是每个线程中MessageQueue的管家,调用Looper的loop()
方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()
方法中。每个线程也只有一个Looper对象。
使用AsyncTask:
AsyncTask是一个抽象类,基于异步消息处理机制从子线程切换到主线程。具有3个泛型参数:Params、Progress、Result
Params :用于在后台任务中传入参数。
Progress :如需显示当前的进度,则使用这里指定的泛型作为进度单位。
Result :任务执行完后,如需对结果进行返回,则指定这里的泛型作为返回值类型。
1 2 3 class DownloadTask extends AsyncTask <void ,Integer ,Boolean > { ... }
重写方法:
onPreExecute()
:在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。
doInBackground(Params...)
:这个方法中的所有代码都会在子线程中执行,在这里处理耗时任务,任务一旦完成就可以通过return语句来将任务的执行结果返回。这个方法不可以进行UI操作。
onProgressUpdate(Progress...)
:当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法就会很快被调用,可进行UI操作。
onPostExecute(Result)
:任务执行完毕并通过return进行返回时,这个方法就会很快被调用。
7.2、服务的基本用法
定义一个服务MyService,该服务继承Service,new——service——service。
重写onCreate()
:服务创建时调用、onStartCommand()
:每次服务启动的时候调用、onDestroy()
:服务销毁的时候调用。
活动与服务通信:
示例:MyService中构建一个下载功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class MyService extends Service { 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 ; } } public MyService () { } @Override public IBinder onBind (Intent intent) { return mBinder; } @Override public void onCreate () { super .onCreate(); Log.d("MyService" ,"onCreate executed" ); } @Override public void onDestroy () { super .onDestroy(); Log.d("MyService" ,"onDestroy executed" ); } @Override public int onStartCommand (Intent intent, int flags, int startId) { Log.d("MyService" ,"onStartCommand exected" ); return super .onStartCommand(intent, flags, startId); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class MainActivity extends AppCompatActivity implements View .OnClickListener { private MyService.DownloadBinder downloadBinder; private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected (ComponentName name, IBinder service) { downloadBinder=(MyService.DownloadBinder)service; downloadBinder.startDownload(); downloadBinder.getProgress(); } @Override public void onServiceDisconnected (ComponentName name) { } }; @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); Button bindService=(Button) findViewById(R.id.bind_service); Button unbindService=(Button) findViewById(R.id.unbind_service); startService.setOnClickListener(this ); stopService.setOnClickListener(this ); bindService.setOnClickListener(this ); unbindService.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 ; default : break ; } } }
7.3、服务的生命周期
一旦调用Context的startService()
方法,服务就会开始启动,并回调onStartCommand()
方法。如果服务还未创建,onCreate()
方法会先于onStartCommand()
方法调用。
服务启动了会一直保持运行状态,直到stopService()
或stopSelf()
方法被调用。每个服务只存在一个实例,无论调用多少次启动,只需调用一次停止,服务就会停止。
在调用stopService()
或unbindService()
方法时,onDestroy()
方法会被执行。当启动和绑定同时被调用,则相应的同时调用停止服务和解绑onDestroy()
方法才会被执行。
7.4、前台服务
服务在系统出现内存不足时可能回收掉正在后台运行的服务。如果希望服务一直保持运行,则需要使用到前台服务。
前台服务会有一个正在运行的图标一直在系统状态栏显示,类似于通知的效果。
1 2 <uses-permission android:name ="android.permission.INTERNET" /> <uses-permission android:name ="android.permission.FOREGROUND_SERVICE" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class MyService extends Service { private NotificationManager notificationManager; private Notification notification; private NotificationChannel notificationChannel; private final static String NOTIFICATION_CHANNEL_ID = "CHANNEL_ID" ; private final static String NOTIFICATION_CHANNEL_NAME = "CHANNEL_NAME" ; private final static int FOREGROUND_ID=1 ; .... @Override public int onStartCommand (Intent intent, int flags, int startId) { Log.d("MyService" ,"onStartCommand exected" ); super .onStartCommand(intent, flags, startId); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH ); notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(notificationChannel); } intent=new Intent(this ,MainActivity.class); PendingIntent pendingIntent=PendingIntent.getActivity(this ,0 ,intent,0 ); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){ notification=new Notification.Builder(this ,NOTIFICATION_CHANNEL_ID).setContentTitle("今日天气" ).setContentText(" 天气清和、微风不噪" ).setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)).setContentIntent(pendingIntent).build(); } notification.flags |= Notification.FLAG_NO_CLEAR; startForeground(1 ,notification); return Service.START_STICKY; } }