Android笔记
工程目录结构 └── HelloWorld ├── app 项目代码,资源 │ ├── build 编译自动生成 │ ├── build.gradle │ ├── libs 项目中引用的第三方jar包 │ ├── proguard-rules.pro 加密混淆,防逆向破解 │ └── src 资源目录 ├── build.gradle 项目全局的gradle构架脚本 ├── gradle gradle wrapper配置文件 │ └── wrapper ├── gradle.properties gradle 全局配置文件 ├── gradlew ├── gradlew.bat ├── local.properties 指定本机中android sdk路径 └── settings.gradle 指定项目中所有引入的模块
资源目录src
├── androidTest 测试用例 │ └── java │ └── com ├── main │ ├── AndroidManifest.xml 整个android项目配置文件 │ ├── java │ │ └── com │ └── res 资源目录 │ ├── drawable 放图片,drawable开头 │ ├── drawable-v24 │ ├── layout 布局文件 │ ├── mipmap-anydpi-v26 由mipmap开头的都是用来放图标的 │ ├── mipmap-hdpi │ ├── mipmap-mdpi │ ├── mipmap-xhdpi │ ├── mipmap-xxhdpi │ ├── mipmap-xxxhdpi │ ├── values │ └── values-night └── test 用于Unit test └── java └── com
res目录中定义的xml文件可以在新的xml文件或者java代码中被引用
例如res下有一个 strings.xml 文件
1 2 3 <resources > <string name ="app_name" > HelloWorldApplication</string > </resources >
则在AndroidManifest.xml文件中作为顶栏应用名显示
1 android:label="@string/app_name"
而在代码中引用则用R.String.app_name
安卓的log log.v() verbose
log.d() debug
log.i() info
log.w() warinning
log.e() error
直接类比输出语句,输出在logcat中
activity 活动是一种可以包含用户界面的组件,用于和用户交互
1 2 3 4 5 Toast.makeText(this , "toast显示的内容" , Toast.LENGTH_SHORT).show();
菜单使用方法 在res文件夹中创建menu目录,并且创建一个新的资源文件(main.xml)
@+id/test
是注册一个新的id选项,可以被java调用
R.id.test
1 2 3 4 5 6 7 8 9 10 <?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 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public boolean onCreateOptionsMenu (Menu menu) { getMenuInflater().inflate(R.menu.main,menu); return true ; } @Override public boolean onOptionsItemSelected (@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.add_item: Toast.makeText(this , "you clicked add" , Toast.LENGTH_SHORT).show(); break ; case R.id.remove_item: Toast.makeText(this , "you click Remove" , Toast.LENGTH_SHORT).show(); break ; default : } return true ; }
销毁活动只需一个函数finsh()
intent启动activity 显式调用 1 2 3 4 5 6 7 8 9 10 Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(firstActivity.this ,secondActivity.class); startActivity(intent); } });
隐式调用 为创建好的第二个活动注册
1 2 3 4 5 6 <activity android:name =".secondActivity" > <intent-filter > <action android:name ="com.example.activitytest.ACTION_START" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter > </activity >
在firstActivity.java中调用
1 2 3 4 5 6 7 8 9 Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent("com.example.activitytest.ACTION_START" ); startActivity(intent); } });
使用隐式调用其他系统功能
1 2 3 4 5 6 7 8 9 10 11 12 13 Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http://www.baidu.com" )); Intent intent = new Intent(Intent.ACTION_TEL); intent.setData(Uri.parse("tel:10086" )); startActivity(intent); } });
intent数据传递 向下一个活动传递 1 2 3 4 5 6 7 8 9 10 Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(firstActivity.this ,secondActivity.class); intent.putExtra("extra_Data" ,"sent" ); startActivity(intent); } });
1 2 3 4 5 6 7 8 9 @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_second); 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 26 27 28 29 30 31 @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 v) { Intent intent = new Intent(firstActivity.this ,secondActivity.class); startActivityForResult(intent,1 ); } }); } @Override protected void onActivityResult (int requestCode, int resultCode, @Nullable Intent data) { switch (requestCode){ case 1 : if (resultCode == RESULT_OK){ String returnData = data.getStringExtra("data_return" ); Log.d("firstActivity" , returnData); } break ; default : } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 setContentView(R.layout.activity_second); Button button2 = (Button) findViewById(R.id.button2); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(); intent.putExtra("data_return" ,"hello firstactivity" ); setResult(RESULT_OK,intent) ; finish(); } }); @Override public void onBackPressed () { Intent intent = new Intent(); intent.putExtra("data_return" ,"hello firstacticity" ); setResult(RESULT_OK,intent); finish(); }
UI 控件 在LinearLayout布局的情况下,只有android:orientation=”vertical”才可以不重叠的显示
TextView
1 2 3 4 5 6 7 8 <TextView android:layout_width ="match_parent" android:layout_height ="wrap_content" android:gravity ="center" android:textSize ="24sp" android:textColor ="#00ff00" android:text ="this is Text view" />
Button
1 2 3 4 5 6 7 8 <Button android:id ="@+id/button" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="Button" android:textAllCaps ="false" />
EditText
1 2 3 4 5 6 <EditText android:id ="@+id/edit_text" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:hint ="type atuoa" android:maxLines ="2" />
hint指的是未输入文字之前的提示
maxline表示的是最大显示的行数
ImageView
1 2 3 4 5 6 <ImageView android:id ="@+id/image_view" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:src ="@drawable/img_1" />
src为资源目录下的图片
ProgressBar
转圈圈进度条
1 2 3 4 5 <ProgressBar android:id ="@+id/progress_bar" android:layout_width ="match_parent" android:layout_height ="wrap_content" />
1 2 3 ProgressBar progress_bar = (ProgressBar)findViewById(R.id.progress_bar);
1 2 3 4 5 6 7 progress_bar.getVisibility() == View.VISIBLE progress_bar.setVisibility(View.GONE); progress_bar.setVisibility(View.VISIBLE);
水平进度条
1 2 3 4 5 6 7 <ProgressBar android:id ="@+id/progress_bar" android:layout_width ="match_parent" android:layout_height ="wrap_content" style ="?android:attr/progressBarStyleHorizontal" android:max ="100" />
1 2 3 4 int progerss = progressBar.getProgress();progeress = Progress +10 ; progressBar.setProgress(progress);
AlertDialog
点击按钮弹出对话框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this ); dialog.setTitle("this is dialog" ); dialog.setMessage("context" ); dialog.setCancelable(false ); dialog.setPositiveButton("ok" , new DialogInterface.OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { } }); dialog.setNegativeButton("cancel" , new DialogInterface.OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { } }); dialog.show(); } });
ProgressDialog
1 2 3 4 5 6 7 ProgressDialog progressDialog = new ProgressDialog(MainActivity.this ); progressDialog.setTitle("this is progressdialog" ); progressDialog.setMessage("loading.." ); progressDialog.setCancelable(true ); progressDialog.show();
四种布局 LinearLayout edittext自适应,button仅包裹文字
1 2 3 4 5 6 7 8 9 10 11 12 13 <EditText android:id ="@+id/edit_text" android:layout_width ="0dp" android:layout_height ="wrap_content" android:layout_weight ="1" android:hint ="type atuoa" /> <Button android:id ="@+id/button" android:layout_width ="warp_content" android:layout_height ="wrap_content" android:text ="Button" />
edittext:button = 3:2比例布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <EditText android:id ="@+id/edit_text" android:layout_width ="0dp" android:layout_height ="wrap_content" android:layout_weight ="3" android:hint ="type atuoa" /> <Button android:id ="@+id/button" android:layout_width ="0dp" android:layout_weight ="2" android:layout_height ="wrap_content" android:text ="Button" />
RelativeLayout
相对父元素布局
android:layout_alignParentButtom
android:layout_alignParentTop
android:layout_alignParentLeft
android:layout_alignParentRight
android:layout_centerInParent
取值均为布耳值
相对控件布局
android:layout_above=”@id/id值”
android:layout_below=”@id/id值”
android:layout_toRightOf=”@id/id值”
android:layout_toLeftOf=”@id/id值”
ps:当一个控件引用另一个控件id时必须要定义在引用控件的后面。
FrameLayout PercentFrameLayout 在app/build.gradle文件的dependencies
添加 compile 'com.android.support:percent:24.2.1'
sync now
例子:一屏占满四个按钮
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 <Button android:id ="@+id/button1" android:layout_gravity ="left | top" app:layout_wedthPercent ="50%" app:layout_heightPercent ="50%" android:text ="Button1" /> <Button android:id ="@+id/button2" android:layout_gravity ="right | top" app:layout_wedthPercent ="50%" app:layout_heightPercent ="50%" android:text ="Button2" /> <Button android:id ="@+id/button3" android:layout_gravity ="left | bottom" app:layout_wedthPercent ="50%" app:layout_heightPercent ="50%" android:text ="Button3" /> <Button android:id ="@+id/button4" android:layout_gravity ="right | buttom" app:layout_wedthPercent ="50%" app:layout_heightPercent ="50%" android:text ="Button4" />
自定义系统控件
将写好的标题栏xml放入layout文件夹中,假设为title.xml
在activity_main.xml布局文件中添加
<include layout = "layout/title">
把系统自带的标题栏隐藏
1 2 3 4 5 ActionBar Actionbar = getSupportAcitonBar(); if (actionbar != null ) actionbar.hide();
在title.xml已经建立好的情况下,新建一个TitleLayout继承自LinearLayout,让他成为自定义的标签栏控件
1 2 3 4 5 6 public class TitleLinearout extends LinearLayout { public TitileLayout (Context context , AttributeSet attrs) super (context,attrs) ; LayoutInflater.from(context).inflate(R.layout.title,this ); }
在布局文件中添加自定义控件
1 2 3 4 <com.example.uicustomviews.TitleLayuot > android:layout_width="match_parent" android:layout+_height="warp_content" />
在事件触发的时候直接调用就行
ListView activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" > <ListView android:id ="@+id/list_view" android:layout_width ="match_parent" android:layout_height ="match_parent" /> </LinearLayout >
//MainAcitivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private String[] data = { "1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ,"8" ,"9" ,"10" , "1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ,"8" ,"9" ,"10" }; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayAdapter<String> adapter = new ArrayAdapter<String>( MainActivity.this , android.R.layout.simple_list_item_1,data ); ListView listview = (ListView) findViewById(R.id.list_view); listview.setAdapter(adapter); }
// listview点击事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private String[] data = { "1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ,"8" ,"9" ,"10" , "1" ,"2" ,"3" ,"4" ,"5" ,"6" ,"7" ,"8" ,"9" ,"10" }; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayAdapter<String> adapter = new ArrayAdapter<String>( MainActivity.this , android.R.layout.simple_list_item_1,data ); ListView listview = (ListView) findViewById(R.id.list_view); listview.setAdapter(adapter); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick (AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this , position+" is clicked " ,Toast.LENGTH_SHORT).show(); } });
RecyclerView 文件 context类使用java文件流读写文件 懒的看,用到时再写,不如用sharePreferences
SharePreferences(通过键值对存储数据) 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 Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { SharedPreferences.Editor editor = getSharedPreferences("data" ,MODE_PRIVATE).edit(); editor.putString("name" ,"tom" ); editor.putInt("age" ,23 ); editor.putBoolean("married" ,false ); editor.apply(); } }); Button button1 = (Button)findViewById(R.id.restore_button); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { String TAG = "mylog" ; SharedPreferences pref = getSharedPreferences("data" ,MODE_PRIVATE); String name = pref.getString("name" ,"" ); int age = pref.getInt("age" ,1 ); boolean married = pref.getBoolean("married" ,true ); Log.d(TAG, "name is " +name); Log.d(TAG, "age is " +age); Log.d(TAG, "married is" +married); } });
数据库 adb操作
adb shell进入调试
su提权获得root权限
cd到程序文件目录,一般为/data/data/包名/
sqlite操作
sqlite3 databse.db
进入名为database.db的数据库
.table
列出表 .schema
列出所有表结构 .exit
退出sqlite交互式命令
查询 select * from table_name
litepal操作sqlite数据库
配置
1. Include library Edit your build.gradle file and add below dependency.
1 2 3 dependencies { implementation 'org.litepal.guolindev:core:3.2.2' }
Create a file in the assets folder of your project and name it as litepal.xml . Then copy the following codes into it.
再项目文件夹下右键新建一个asset folder - 然后添加一个文件命名为litepal.xml,复制粘贴以下内容
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 <?xml version="1.0" encoding="utf-8"?> <litepal > <dbname value ="demo" /> <version value ="1" /> <list > <mapping class ="包名.类名" /> </list > </litepal >
This is the only configuration file, and the properties are simple.
dbname configure the database name of project.
version configure the version of database. Each time you want to upgrade database, plus the value here.
list configure the mapping classes.
storage configure where the database file should be stored. internal and external are the only valid options.
You don’t want to pass the Context param all the time. To makes the APIs simple, just configure the LitePalApplication in AndroidManifest.xml as below:
1 2 3 4 5 6 7 8 <manifest > <application android:name ="org.litepal.LitePalApplication" ... > ... </application > </manifest >
Of course you may have your own Application and has already configured here, like:
1 2 3 4 5 6 7 8 <manifest > <application android:name ="com.example.MyOwnApplication" ... > ... </application > </manifest >
That’s OK. LitePal can still live with that. Just call LitePal.initialize(context) in your own Application:
1 2 3 4 5 6 7 8 9 public class MyOwnApplication extends Application { @Override public void onCreate () { super .onCreate(); LitePal.initialize(this ); } ... }
Make sure to call this method as early as you can. In the onCreate() method of Application will be fine. And always remember to use the application context as parameter. Do not use any instance of activity or service as parameter, or memory leaks might happen.
使用
创建一个类,名字就为mapping中的类名,
假设有一个类book,
1 2 3 4 5 6 7 8 public class Book extends LitePalSupport { private int id; private double price; private String author; private int pages; private String name; private String press;
然后再MainActivity中Connector.getDatabase();
即完成了对数据库的创建,结构就是Book中定义的
Context Provider 主要用于在不同的应用程序之间的数据共享功能,它提供一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问程序的安全性。
运行时权限
安卓6开始对使用的运行时权限,在程序运行时对权限提出申请
例子拨打电话 1 2 <uses-permission android:name ="android.permission.CALL_PHONE" />
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 @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) { 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(); default : } }
例子显示联系人列表 1 2 <uses-permission android:name ="android.permission.READ_CONTACTS" />
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 ArrayAdapter<String> adapter; List<String> contactsList = 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,contactsList); 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(); } private 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)); contactsList.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) { 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 : } }
自定义内容提供器 用于不同应用之间数据库互相访问的接口。
参考书7.4
通知 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 @RequiresApi(api = Build.VERSION_CODES.O) @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); createNotificationChannel(); Button sendNotice =(Button) findViewById(R.id.send_notice); sendNotice.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this , "text" ) .setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("test" ) .setContentText("this is a test notification" ) .setPriority(NotificationCompat.PRIORITY_DEFAULT); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this ); notificationManager.notify(1 , builder.build()); } }); } @RequiresApi(api = Build.VERSION_CODES.O) private void createNotificationChannel () { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence name = "test" ; String description = "test" ; int importance = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel = new NotificationChannel("text" , name, importance); channel.setDescription(description); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } }
webview MainActivity.java
1 2 3 4 5 6 WebView webView = findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true ); webView.setWebViewClient(new WebViewClient()); webView.loadUrl("https://www.dogedoge.com" );
manifest.xml
1 <uses-permission android:name ="android.permission.INTERNET" />
activity_main.xml
1 2 3 4 5 <WebView android:id ="@+id/web_view" android:layout_width ="match_parent" android:layout_height ="match_parent" />
httpConnection
原生实现
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 TextView responseText; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button sendRequest = (Button) findViewById(R.id.send_request); responseText = (TextView) findViewById(R.id.response_text); sendRequest.setOnClickListener(this ); } @Override public void onClick (View v) { if (v.getId() == R.id.send_request) sendrequestWithHttpURLConnection(); } private void sendrequestWithHttpURLConnection () { new Thread(new Runnable() { @Override public void run () { HttpURLConnection connection = null ; BufferedReader reader = null ; try { URL url = new URL("https://www.dogedoge.com" ); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET" ); connection.setConnectTimeout(8000 ); connection.setReadTimeout(8000 ); InputStream in = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(in)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null ) response.append(line); showResponse(response.toString()); }catch (Exception e) { e.printStackTrace(); }finally { if (reader!=null ) { try { reader.close(); }catch (IOException e) { e.printStackTrace(); } } if (connection != null ) connection.disconnect(); } } }).start(); } private void showResponse (final String response) { runOnUiThread(new Runnable() { @Override public void run () { responseText.setText(response); } }); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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/send_request" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="send request" /> <ScrollView android:layout_width ="match_parent" android:layout_height ="match_parent" > <TextView android:id ="@+id/response_text" android:layout_width ="match_parent" android:layout_height ="wrap_content" /> </ScrollView > </LinearLayout >
okhttp实现
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 TextView responseText; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button sendRequest = (Button) findViewById(R.id.send_request); responseText = (TextView) findViewById(R.id.response_text); sendRequest.setOnClickListener(this ); } @Override public void onClick (View v) { if (v.getId() == R.id.send_request) sendRequestWithOkHttp(); } private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://www.dogedoge.com" ) .build(); Response response = client.newCall(request).execute(); String responeseData = response.body().string(); showResponse(responeseData); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void showResponse (final String response) { runOnUiThread(new Runnable() { @Override public void run () { responseText.setText(response); } }); }
解析xml pull解析 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 private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://10.0.2.2:88/get_data.xml" ) .build(); Response response = client.newCall(request).execute(); String responseData = response.body().string(); parseXMLWithPull(responseData); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void parseXMLWithPull (String xmlData) { try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); xmlPullParser.setInput(new StringReader(xmlData)); int eventType = xmlPullParser.getEventType(); String id="" ; String name ="" ; String version = "" ; while (eventType != xmlPullParser.END_DOCUMENT) { String nodename = xmlPullParser.getName(); switch (eventType) { case XmlPullParser.START_TAG: { if ("id" .equals(nodename)) id = xmlPullParser.nextText(); else if ("name" .equals(nodename)) name = xmlPullParser.nextText(); else if ("version" .equals(nodename)) version = xmlPullParser.nextText(); break ; } case XmlPullParser.END_TAG: { if ("app" .equals(nodename)) { Log.d("MainActivity" , "id is" +id); Log.d("MainActivity" , "name is" +name); Log.d("MainActivity" , "version is" +version); } break ; } default : break ; } eventType = xmlPullParser.next(); } }catch (Exception e) { e.printStackTrace(); } }
sax解析 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 private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://10.0.2.2:88/get_data.xml" ) .build(); Response response = client.newCall(request).execute(); String responseData = response.body().string(); parseXMLWithSAX(responseData); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void parseXMLWithSAX (String xmlData) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader xmlReader = factory.newSAXParser().getXMLReader(); ContentHandler handler= new ContentHandler(); xmlReader.setContentHandler(handler); xmlReader.parse(new InputSource(new StringReader(xmlData))); }catch (Exception e) { e.printStackTrace(); } }
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 public class ContentHandler extends DefaultHandler { private String nodeName; private StringBuilder id; private StringBuilder name; private StringBuilder version; @Override public void characters (char [] ch, int start, int length) throws SAXException { if ("id" .equals(nodeName)) id.append(ch,start,length); else if ("name" .equals(nodeName)) name.append(ch,start,length); else if ("version" .equals(nodeName)) version.append(ch, start, length); } @Override public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException { nodeName = localName; } @Override public void endDocument () throws SAXException { super .endDocument(); } @Override public void endElement (String uri, String localName, String qName) throws SAXException { if ("app" .equals(localName)) { Log.d("ContentHandler" , "id is " +id.toString().trim() ); Log.d("ContentHandler" , "name is " +name.toString().trim() ); Log.d("ContentHandler" , "version is " +version.toString().trim() ); id.setLength(0 ); name.setLength(0 ); version.setLength(0 ); } } @Override public void startDocument () throws SAXException { id = new StringBuilder(); name = new StringBuilder(); version = new StringBuilder(); } }
解析json JSONobject解析 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 private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://10.0.2.2:88/get_data.json" ) .build(); Response response = client.newCall(request).execute(); String responseData = response.body().string(); parseJSONWithJSONObject(responseData); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void parseJSONWithJSONObject (String jsonData) { try { JSONArray jsonArray = new JSONArray(jsonData); for (int i=0 ;i<jsonArray.length();i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String id = jsonObject.getString("id" ); String name = jsonObject.getString("name" ); String version = jsonObject.getString("version" ); Log.d("MainActivity" , "id is" +id); Log.d("MainActivity" , "name is" +name); Log.d("MainActivity" , "version is" +version); } }catch (Exception e) {e.printStackTrace();} }
GSON解析 1 implementation 'com.google.code.gson:gson:2.8.6'
新建一个App类
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 package com.example.networktest;public class App { private StringBuilder id; private StringBuilder name; private StringBuilder version; public void setId (StringBuilder id) { this .id = id; } public void setName (StringBuilder name) { this .name = name; } public void setVersion (StringBuilder version) { this .version = version; } public StringBuilder getId () { return id; } public StringBuilder getName () { return name; } public StringBuilder getVersion () { return version; } }
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 private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://10.0.2.2:88/get_data.json" ) .build(); Response response = client.newCall(request).execute(); String responseData = response.body().string(); parseJSONWithGSON(responseData); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private void parseJSONWithGSON (String jsonData) { Gson gson = new Gson(); List <App> appList = gson.fromJson(jsonData,new TypeToken<List<App>>(){}.getType()); for (App app: appList) { Log.d("MainActivity" , "id is" +app.getId()); Log.d("MainActivity" , "name is" +app.getName()); Log.d("MainActivity" , "version is" +app.getVersion()); } }
封装网络请求 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 @Override public void onClick (View v) { if (v.getId() == R.id.send_request) sendRequestWithOkHttp(); } private void sendRequestWithOkHttp () { new Thread(new Runnable() { @Override public void run () { try { String address = "http://www.baidu.com" ; Httputil.sendOkHttpRequest(address,new Callback() { @Override public void onFailure (@NotNull Call call, @NotNull IOException e) { } @Override public void onResponse (@NotNull Call call, @NotNull Response response) throws IOException { try { String responseData = response.body().string(); Log.d("Main" , responseData); } catch (Exception e1) { e1.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } }).start(); }
服务 例子,子线程更改ui,安卓多线程 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 48 49 public class MainActivity extends AppCompatActivity implements View .OnClickListener { private TextView text; public static final int UPDATE_TEXT = 1 ; @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 ); } private Handler handler = new Handler() { public void handleMessage (Message msg) { switch (msg.what){ case UPDATE_TEXT: text.setText("nice to meet you" ); break ; default : break ; } } }; @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是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。上一小节中我们使用到了Message的what
字段,除此之外还可以使用arg1
和arg2
字段来携带一些整型数据,使用obj
字段携带一个Object
对象。
Handler
Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()
方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()
方法中。
MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue
对象。
Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop()
方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()
方法中。每个线程中也只会有一个Looper
对象。
了解了Message、Handler、MessageQueue以及Looper的基本概念后,我们再来把异步消息处理的整个流程梳理一遍。首先需要在主线程当中创建一个Handler
对象,并重写handleMessage()
方法。然后当子线程中需要进行UI操作时,就创建一个Message
对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()
方法中。由于Handler是在主线程中创建的,所以此时handleMessage()
方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行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 public class MainActivity extends AppCompatActivity implements View .OnClickListener { @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 ); } @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 ; default : break ; } }
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 public class MyService extends Service { public MyService () { } @Override public IBinder onBind (Intent intent) { throw new UnsupportedOperationException("Not yet implemented" ); } @Override public void onCreate () { super .onCreate(); Log.d("MyService" , "onCreate: " ); } @Override public int onStartCommand (Intent intent, int flags, int startId) { Log.d("MyService" , "onStartCommand: " ); return super .onStartCommand(intent, flags, startId); } @Override public void onDestroy () { super .onDestroy(); Log.d("MyService" , "onDestroy: " ); } }
活动服务通信绑定(bind) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { public void startDownload () { Log.d("MyService" , "startDownload: " ); } public int getProgress () { Log.d("MyService" , "getProgress: " ); return 0 ; } } @Override public IBinder onBind (Intent intent) { return mBinder; }
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 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) { } }; 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 ;
前台服务 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 public class MyService extends Service { private static final String CHANNEL_ID = "1" ; private static final int NOTIFICATION_ID = 1 ; @androidx .annotation.RequiresApi(api = Build.VERSION_CODES.O) public void setForegroundService () { String channelName = "hello" ; int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, importance); channel.setDescription("test" ); NotificationCompat.Builder builder = new NotificationCompat.Builder(this , CHANNEL_ID); builder.setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("notificationTitle" ) .setContentText("notificationContent" ) .setAutoCancel(true ) .setOngoing(true ); NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); startForeground(NOTIFICATION_ID,builder.build()); } private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { public void startDownload () { Log.d("MyService" , "startDownload: " ); } public int getProgress () { Log.d("MyService" , "getProgress: " ); return 0 ; } } public MyService () { } @Override public IBinder onBind (Intent intent) { return mBinder; } @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onCreate () { super .onCreate(); Log.d("MyService" , "onCreate: " ); setForegroundService(); } @Override public int onStartCommand (Intent intent, int flags, int startId) { Log.d("MyService" , "onStartCommand: " ); return super .onStartCommand(intent, flags, startId); } @Override public void onDestroy () { super .onDestroy(); Log.d("MyService" , "onDestroy: " ); } }
1 2 <uses-permission android:name ="android.permission.FOREGROUND_SERVICE" />
安卓多线程 服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现ANR(Application Not Responding)的情况。
所以这个时候就需要用到Android多线程编程的技术了,我们应该在服务的每个具体的方法里开启一个子线程,然后在这里去处理那些耗时的逻辑。因此,一个比较标准的服务就可以写成如下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MyService extends Service { ... @Override public int onStartCommand (Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run () { } }).start(); return super .onStartCommand(intent, flags, startId); } }
但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用stopService()
或者stopSelf()
方法才能让服务停止下来。所以,如果想要实现让一个服务在执行完毕后自动停止的功能,就可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MyService extends Service { ... @Override public int onStartCommand (Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run () { stopSelf(); } }).start(); return super .onStartCommand(intent, flags, startId); } }
使用IntentSevice来管理线程 自动开启和关闭线程
1 2 3 Intent intentService = new Intent(this ,MyIntentService.class); startService(intentService);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MyIntentService extends IntentService { public MyIntentService () { super ("MyIntentService" ); } @Override protected void onHandleIntent (@Nullable Intent intent) { Log.d("MyIntentService" , "Thread id is " +Thread.currentThread().getId()); } @Override public void onDestroy () { super .onDestroy(); Log.d("MyIntentService" , "onDestroy executed" ); } }
百度定位SDK 获取经纬度信息,地址描述 导入第三方jar包放在/app/libs目录后需要右键鼠标add as library..
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public LocationClient mLocationClient; private TextView positionText; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); mLocationClient = new LocationClient(getApplicationContext()); mLocationClient.registerLocationListener(new MyLocationListener()); setContentView(R.layout.activity_main); positionText = (TextView) findViewById(R.id.position_text_view); List<String> permissionList = new ArrayList<>(); if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION); } if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.READ_PHONE_STATE); } if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } if (!permissionList.isEmpty()) { String [] permissions = permissionList.toArray(new String[permissionList. size()]); ActivityCompat.requestPermissions(MainActivity.this , permissions, 1 ); } else { requestLocation(); } } @Override public void onRequestPermissionsResult (int requestCode, @NonNull String[] permissions, @NonNull int [] grantResults) { switch (requestCode) { case 1 : if (grantResults.length > 0 ) { for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this , "必须同意所有权限才能使用本程序" , Toast.LENGTH_SHORT).show(); finish(); return ; } } requestLocation(); } else { Toast.makeText(this , "发生未知错误" , Toast.LENGTH_SHORT).show(); finish(); } break ; default : } } private void initLocation () { LocationClientOption option = new LocationClientOption(); option.setScanSpan(5000 ); option.setIsNeedAddress(true ); mLocationClient.setLocOption(option); } private void requestLocation () { initLocation(); mLocationClient.start(); } public class MyLocationListener extends BDAbstractLocationListener { @Override public void onReceiveLocation (BDLocation location) { runOnUiThread(new Runnable() { @Override public void run () { StringBuilder currentPosition = new StringBuilder(); currentPosition.append("纬度:" ).append(location.getLatitude()). append("\n" ); currentPosition.append("经线:" ).append(location.getLongitude()). append("\n" ); currentPosition.append("国家:" ).append(location.getCountry()). append("\n" ); currentPosition.append("省:" ).append(location.getProvince()). append("\n" ); currentPosition.append("市:" ).append(location.getCity()). append("\n" ); currentPosition.append("区:" ).append(location.getDistrict()). append("\n" ); currentPosition.append("街道:" ).append(location.getStreet()). append("\n" ); currentPosition.append("定位方式:" ); if (location.getLocType() == BDLocation.TypeGpsLocation) { currentPosition.append("GPS" ); } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) { currentPosition.append("网络" ); } positionText.setText(currentPosition); } }); } } @Override protected void onDestroy () { super .onDestroy(); mLocationClient.stop(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <uses-permission android:name ="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name ="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name ="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name ="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name ="android.permission.READ_PHONE_STATE" /> <uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name ="android.permission.INTERNET" /> <uses-permission android:name ="android.permission.WAKE_LOCK" /> <service android:name ="com.baidu.location.f" android:enabled ="true" android:process =":remote" > </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 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 public class MainActivity extends AppCompatActivity { private BaiduMap baiduMap; private boolean isFirstLocate = true ; private MapView mapView; public LocationClient mLocationClient; private void navigateTo (BDLocation location) { if (isFirstLocate) { LatLng ll = new LatLng(location.getLatitude(), location.getLongitude()); MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll); baiduMap.animateMapStatus(update); update = MapStatusUpdateFactory.zoomTo(16f ); baiduMap.animateMapStatus(update); isFirstLocate = false ; } MyLocationData.Builder locationBuilder = new MyLocationData.Builder(); locationBuilder.latitude(location.getLatitude()); locationBuilder.longitude(location.getLongitude()); MyLocationData locationData = locationBuilder.build(); baiduMap.setMyLocationData(locationData); } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); mLocationClient = new LocationClient(getApplicationContext()); mLocationClient.registerLocationListener(new MyLocationListener()); SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); mapView = (MapView) findViewById(R.id.bmapView) ; baiduMap = mapView.getMap(); baiduMap.setMyLocationEnabled(true ); List<String> permissionList = new ArrayList<>(); if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION); } if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.READ_PHONE_STATE); } if (ContextCompat.checkSelfPermission(MainActivity.this , Manifest. permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) { permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } if (!permissionList.isEmpty()) { String [] permissions = permissionList.toArray(new String[permissionList. size()]); ActivityCompat.requestPermissions(MainActivity.this , permissions, 1 ); } else { requestLocation(); } } @Override protected void onResume () { super .onResume(); mapView.onResume(); } @Override protected void onPause () { super .onPause(); mapView.onPause(); } @Override public void onRequestPermissionsResult (int requestCode, @NonNull String[] permissions, @NonNull int [] grantResults) { switch (requestCode) { case 1 : if (grantResults.length > 0 ) { for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this , "必须同意所有权限才能使用本程序" , Toast.LENGTH_SHORT).show(); finish(); return ; } } requestLocation(); } else { Toast.makeText(this , "发生未知错误" , Toast.LENGTH_SHORT).show(); finish(); } break ; default : } } private void initLocation () { LocationClientOption option = new LocationClientOption(); option.setScanSpan(5000 ); option.setIsNeedAddress(true ); mLocationClient.setLocOption(option); } private void requestLocation () { initLocation(); mLocationClient.start(); } public class MyLocationListener extends BDAbstractLocationListener { @Override public void onReceiveLocation (BDLocation location) { navigateTo(location); } } @Override protected void onDestroy () { super .onDestroy(); mLocationClient.stop(); mapView.onDestroy(); baiduMap.setMyLocationEnabled(false ); } }
杂项 ctrl + o 快速重载
alt + insert 自动补全get,set方法
adb & fastboot command 常用 ADB 命令
代码
含义
adb reboot bootloader
在 bootloader 模式下重启
adb push
将文件从本地系统复制到 Android 手机的位置
adb pull
将文件从 Android 复制到您的系统
adb devices
显示所有连接的 adb 兼容设备
adb backup
备份 Android 设备
adb install
将应用程序从系统的 apk 文件位置安装到 Android 设备上
adb reboot
在正常模式下重新启动 Android 手机
adb connect
通过 WiFi 网络使用adb命令
adb shell screencap
获取设备的屏幕截图
常用 Fastboot 命令
代码
含义
fastboot devices
显示连接的 Android 设备的序列号
fastboot oem unlock
解开 bootloader 锁(Android 5.0 及以下)
fastboot oem lock
恢复 bootloader 锁(Android 5.0 及以下)
fastboot flashing unlock
解开 bootloader 锁(Android 6.0 及以上)
fastboot flashing lock
恢复 bootloader 锁
fastboot flash recovery (filename)
在 bootloader 模式中向设备刷入文件
fastboot oem device-info
查看bootloader锁
fastboot刷写需要root权限
一般刷机直接根据厂商提供的shell脚本一把梭完事
1 2 3 adb shell pm list packages //应用程序列表 adb push [文件] /sdcard/Download //将文件推送到download目录 pm uninstall -k --user 0 [package_name] //为用户卸载应用程序
网络连接adb
adb connect 设备地址
刷第三方recovery
1 2 # 需要root权限 fastboot flash recovery xxx.img
1 2 3 4 5 6 7 8 9 adb backup -f <filename> --twrp <options> <filename> 是备份数据文件名,如果不填则为默认名称 backup.ab; <options> 包含下列命令: --compress: 对备份文件进行压缩 system: 备份根目录下 system 数据 cache: 备份根目录下 cache 数据 data: 备份根目录下 data 数据 boot: 备份根目录下 boot 数据 (其他分区同理)
避免在主线程发送http 请求修改ui 启动一个子线程进行网路请求操作,然后再用Handler更新界面
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 void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); this .setContentView(R.layout.share_mblog_view); new Thread(runnable).start(); } Handler handler = new Handler(){ @Override public void handleMessage (Message msg) { super .handleMessage(msg); Bundle data = msg.getData(); String val = data.getString("value" ); Log.i("mylog" ,"请求结果-->" + val); } } Runnable runnable = new Runnable(){ @Override public void run () { Message msg = new Message(); Bundle data = new Bundle(); data.putString("value" ,"请求结果" ); msg.setData(data); handler.sendMessage(msg); } }
android 9以上开启定位服务 1 2 3 4 5 6 Enable: adb shell settings put secure location_mode 3 Disable: adb shell settings put secure location_mode 0
1 2 3 4 5 6 # 模拟滑动解锁 adb shell input swipe 300 300 500 1000 100 # 输入密码 adb shell input text 1234 # 或者模拟输入 input keyevent KEYCODE_5
adb shell 调用autojs(root情况下)
am start -n org.autojs.autojs/.external.open.RunIntentActivity -d "file:///storage/emulated/0/脚本/test.js"
autojs 向tasker传值
1 2 3 4 5 6 7 8 9 10 11 auto console .show()app.sendBroadcast({ action : "autojs.intent.action.MAIN" , extras : { from : "Autoxjs" , version : "5.3.0" , info :"you get it" , }, });
然后tasker中配置文件选择收到的意图,操作填autojs.intent.action.MAIN
然后指向一个可以显示变量的控件,通过%from , %version , %info取出