android多线程
runOnUiThread
这个可以直接在子线程中调用,并且修改ui1
2
3
4
5
6
7
8public void change(final String a){
runOnUiThread(new Runnable() {
public void run() {
tv.setText(a);
}
});
}
多线程调用1
2
3
4
5
6
7
8
9
10
11new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
change("aaaaaaaaaaaa");
}
}).start();
实现了五秒后更新
Handler
现在主线程中创建一个handler1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18final Handler handler = new Handler(){
public void handleMessage(Message message){
tv.setText(""+message.what);
}
};
然后在子线程中调用handler.sendEmptyMessage(1);就能调用上面的handlemessage方法,传入的1是传入message的what属性,handler.sendMessage(message)方法发送这个message到主线程中的那个回调
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
android retrofit2的基本使用,搭配rxjava
依赖1
2
3
4
5
6
7
8
9
10implementation 'com.google.code.gson:gson:2.6.1'
implementation 'com.orhanobut:logger:2.1.0'
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.4.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
最简单的使用:
先创建接口:1
2
3
4
5
6
7
8public interface WeatherService {
"s6/weather/now") (
Observable<WeatherEntity> getWeather(@Query("key") String key, @Query("location") String location);
"/") (
Call<ResponseBody> baidu();
}
两个get
使用异步获取返回的原生字符串
1 | OkHttpClient client = new OkHttpClient.Builder(). |
使用rxjava
1 | //创建一个连接对象,设置连接超时时间,读超时时间,写超时 |
打印log
1 | HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(); |
让一个okhttp.builder对象client添加以下这个log解析器就ok
自定义结果解析器
自己解析返回的结果GsonConverFactory.create,点进去这个方法,主要看responseBodyConverter方法,这个是处理返回请求的,他是调用了GsonResponseBodyConverter来处理请求,点进去这个类,public T convert(ResponseBody value)这个方法就是返回要构造的对象,参数value就是返回的response的body值,可以通过value.string();得到具体的字符串,在这个方法里解析可以通过throw的方式调用观察者的onError方法
所以重写结果解析器需要复制出来GsonConverFactory这个类,并且复制GsonResponseBodyConverter和GsonRequestBodyConverter这两个类,并且重新写一下GsonResponseBodyConverter这个类的convert方法对结果进行解析
1 |
|
rxjava和rxAndroid
rxJava
RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。
基本创建: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
28Observable<String> oble = Observable.create(new ObservableOnSubscribe<String>() {
public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
e.onNext("hello");
e.onComplete();
e.onNext("hello2");
}
});
Observer<String> oser = new Observer<String>() {
public void onSubscribe(@NonNull Disposable d) {
Log.w("","onSubscribe");
}
public void onNext(@NonNull String s) {
Log.w("","onNext = "+s);
}
public void onError(@NonNull Throwable e) {
Log.w("","onError" + e);
}
public void onComplete() {
Log.w("","onComplete");
}
};
Log.w("","subscribe");
oble.subscribe(oser);
subscribe
onSubscribe
onNext = hello
onComplete
其中oble是一个被观察者,oser是一个观察者,被观察者可以调用onNext向观察者发送内容,此时观察者就能通过重写的onNext获取到数据,执行相应的操作
另外观察者还有oncomplete和onerror,如果执行了onComplete方法,那么就会断开联系,所以hello2没有显示出来,如果发生了错误会调用了onerror也会立马断开联系。
另一些使用方法
简写被观察者
上面的例子是create一个最基本的被观察者,当如果被观察者只有一个动作的时候就不需要那么复杂的操作,可以用一个just1
Observable<String> observable = Observable.just("hello");
这样就是只执行一个onNext(’hello’);
简写观察者
当然对于观察者也是一样,如果不用考虑oncomplete和onerror也可以简写,创建一个consumer对象,重写accept方法就行,然后通过被观察者.subscribe(观察者)来建立联系1
2
3
4
5
6
7
8Observable<String> observable = Observable.just("hello");
Consumer<String> consumer = new Consumer<String>() {
public void accept(String s) throws Exception {
System.out.println(s);
}
};
observable.subscribe(consumer);
创建完成或者错误的另一些方法
可以创建一个action对象来处理oncomplete的事件,用一个consumer来处理onnext和onerror事件,最后重载subscribe的一些方法达到建立关系的目的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Observable<String> observable = Observable.just("hello");
Action onCompleteAction = new Action() {
public void run() throws Exception {
Log.i("kaelpu", "complete");
}
};
Consumer<String> onNextConsumer = new Consumer<String>() {
public void accept(String s) throws Exception {
Log.i("kaelpu", s);
}
};
Consumer<Throwable> onErrorConsumer = new Consumer<Throwable>() {
public void accept(Throwable throwable) throws Exception {
Log.i("kaelpu", "error");
}
};
observable.subscribe(onNextConsumer, onErrorConsumer, onCompleteAction);
public final Disposable subscribe() {}
public final Disposable subscribe(Consumer<? super T> onNext) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
public final void subscribe(Observer<? super T> observer) {}
上面是subscribe一些重载方法
线程调度
rxJava最大的好处就是能够在多线程的情况下去实现,主要能应用在Android更新UI上。。
在建立关系subscribe的时候会有一些方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d("kaelpu", "Observable thread is : " + Thread.currentThread().getName());
Log.d("kaelpu", "emitter 1");
emitter.onNext(1);
}
});
Consumer<Integer> consumer = new Consumer<Integer>() {
public void accept(Integer integer) throws Exception {
Log.d("kaelpu", "Observer thread is :" + Thread.currentThread().getName());
Log.d("kaelpu", "onNext: " + integer);
}
};
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(consumer);
最后那个建立关系的意思是让被监听者在Schedulers.newThread()这个新线程上,然后让观察者在AndroidSchedulers.mainThread()这个主线程上,就实现了主线程更新UI的操作,主要就是subscribeOn是让被观察者运行的线程,observeOn是观察者运行的线程
操作符的使用和Android的一些扩展可以看原文
作者:蒲文辉
链接:https://www.jianshu.com/p/7eb5ccf5ab1e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
android中webview的交互
webview中js调用Java代码
大概思路是写一个Java类,然后通过webview的addJavascriptInterface方法把那个类传到页面中,然后页面就能直接通过指定的名字调用方法
布局就是上面一个webview下面一个textView
- mainactivity.Java
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
27public class MainActivity extends Activity implements JsBridje{
private WebView mWebView;
private TextView mTextView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView)findViewById(R.id.wv_webview);
mTextView = (TextView)findViewById(R.id.tv_result);
//允许webview加载js
mWebView.getSettings().setJavaScriptEnabled(true);
//2.创建一个js接口类,jsinterface
//3.创建一个就是接口类传递到webview中,第一个参数是Java接口类对象,第二个是传入到js中的名称
mWebView.addJavascriptInterface(new JsInterface(this),"javaslei");
//加载要显示的html
mWebView.loadUrl("file:///android_asset/index.html");
}
//重写一个jsbridje接口,让jsinterface类能够调用,改变UI
public void setvalue(String value) {
mTextView.setText(value);
}
}
可以看到调用的addjavascriptinterface方法第一个参数是传入了一个对象,第二个参数是一个字符串,在js中直接调用字符串.方法即可
- JsInterface.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class JsInterface {
private static final String TAG = "JsInterface";
private JsBridje jsBridje;
public JsInterface(JsBridje jsBridje) {
this.jsBridje = jsBridje;
}
/**
* 从js中调用的方法,这个方法不在主线程中执行,所以不能在这里改变UI
* @param value
*/
public void setvalue(String value){
jsBridje.setvalue(value);
}
}
js调用的方法一定要加上@JavascriptInterface
因为要改变页面内容,所以引入了一个什么设计模式实现一jsbridje接口
- JsBridje.java
1
2
3public interface JsBridje {
void setvalue(String value);
}
html1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webview</title>
<script type="text/javascript">
function oclick(){
var inputEle = document.getElementById('uservalue');
if(window.javaslei){
javaslei.setvalue(inputEle.value);
}else{
alert('没找到Java对象');
}
}
</script>
</head>
<body>
<h2>webview</h2>
<div>
<span>请输入要传递的值</span>
<input type="text" id="uservalue" />
<p onclick="oclick()">提交</p>
</div>
</body>
</html>
可以看到,在html中直接调用javaslei(mainactivity传入的名字).setvalue方法就能调用了jsinterface类中的方法,然后这个方法通过调用jsbridje的接口方法去调用mainactivity.java的setvalue方法改变textview的值
java中调用js代码
其实就一行:mWebView.loadUrl(“javascript:要执行的命令”);
布局文件是webview和一个edittext和一个button
- mainactivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class MainActivity extends Activity{
private WebView mWebView;
private EditText meditview;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView)findViewById(R.id.wv_webview);
meditview = (EditText) findViewById(R.id.tv_value);
//允许webview加载js
mWebView.getSettings().setJavaScriptEnabled(true);
//加载要显示的html
mWebView.loadUrl("file:///android_asset/index.html");
}
public void onclick(View view) {
String value = meditview.getText().toString();
mWebView.loadUrl("javascript:if(window.remote){window.remote('"+value+"')}");
}
}
可以看到直接调用了js中的remote方法传入了一个字符串参数,注意那个字符串参数前后要加上单引号
html1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webview</title>
<script type="text/javascript">
function remote(str){
document.write(str);
}
</script>
</head>
<body>
</body>
</html>
html中就一个remote方法,就是把获取到的值写到界面中
使用chrome调试APP中的网页
先启用调试mWebView.setWebContentsDebuggingEnabled(true);
然后chrome访问chrome://inspect/
然后就能看到相应的inspect,点进去就能调试,不过肯定打不开,因为需要翻墙.或者下载离线包
解决了上面的内容就能够像调试网页的方式一样去调试APP中的html代码
一些常见的错误
当js调用java 代码出现了throw,此时APP并不会崩溃,但是会在html的控制台中抛出一个错误
如果js没有判断是否有相应的方法就去调用会出现找不到方法的错误
参数类型错误,常见的有数组和对象里面的问题,因为js是弱类型语言,而Java是强类型,,所以如果类型有错误会导致程序出错
字符串为空值的时候会传入一个字符串类型的undefined,解决办法就是当要传入的对象为空值的时候,传一个””就行
android调用相机和相册
android调用相机拍照
实现点击按钮开始调用相机拍照,并且返回拍照的照片
- mainactivity.java
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
47public class MainActivity extends Activity {
private ImageView imageView;
private Uri imageuri;
private static final int TAKE_PHOTO=1;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)findViewById(R.id.picture);
}
public void one(View view) {
File outputImage = new File(getExternalCacheDir(),"output_image.jpg");
if(outputImage.exists()){
outputImage.delete();
}
try {
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(Build.VERSION.SDK_INT>=24){
imageuri = FileProvider.getUriForFile(MainActivity.this,"com.example.cameraalbumtest.fileprovider",outputImage);
}else{
imageuri = Uri.fromFile(outputImage);
}
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageuri);
startActivityForResult(intent,TAKE_PHOTO);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode){
case TAKE_PHOTO:
if(resultCode==RESULT_OK){
try{
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageuri));
imageView.setImageBitmap(bitmap);
}catch(FileNotFoundException e){
e.printStackTrace();
}
}
}
}
}
布局文件activity_main.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:text="one"
android:onClick="one"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
在sdk24以后还需要需要一个内容提供器,现在清单文件下注册1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.xwmdream.myapplication">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
.......
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.exaample.cameraalibumtest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
</application>
</manifest>
- res/xml/file_paths.xml
1
2
3
4
<pahts xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path=""/>
</pahts>
android调用相册
- MainActivity.java
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
104public class MainActivity extends Activity {
private ImageView imageView;
private Uri imageuri;
private static final int CHOOSE_PHOTO = 2;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.picture);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CHOOSE_PHOTO:
if(resultCode==RESULT_OK){
if(Build.VERSION.SDK_INT>=19){
handleImageOnKitKat(data);
}else{
handleImageBeforeKitKat(data);
}
}
}
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
displayImage(imagePath);
}
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
if(DocumentsContract.isDocumentUri(this,uri)){
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID+"="+id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));
imagePath = getImagePath(contentUri,null);
}
}else if("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(uri,null);
}else if("file".equalsIgnoreCase(uri.getScheme())){
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void displayImage(String imagePath) {
if(imagePath!=null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
imageView.setImageBitmap(bitmap);
}else{
Toast.makeText(this,"打开图片错误",Toast.LENGTH_SHORT).show();
}
}
private String getImagePath(Uri uri, String selection) {
String path = null;
Cursor cursor = getContentResolver().query(uri,null,selection,null,null);
if(cursor != null){
if(cursor.moveToFirst()){
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
public void two(View view) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
openAlbum();
}
}
private void openAlbum() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
}
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAlbum();
} else {
Toast.makeText(this, "没有权限", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
布局文件xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:onClick="two"
android:text="two"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
其中涉及到针对sdk大于19的各个方法验证
还有动态申请权限等问题
当然如果项目中用到的话还需要对于图片文件压缩,否则会内存泄漏
android通知的使用
普通通知
1 | //创建通知管理 |
自定义通知
先创建布局文件
- message.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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="仗剑走天涯"/>
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下一首"/>
</LinearLayout>
Java代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"通知分组");
builder.setSmallIcon(R.mipmap.ic_launcher);
//加载布局
RemoteViews rv = new RemoteViews(getPackageName(), R.layout.message);
rv.setTextViewText(R.id.tv, "泡沫");//修改自定义View中的歌名
//通过pendingIntent发送广播的方式来设置监听事件
Intent button1I = new Intent("btn1");
PendingIntent button1PI = PendingIntent.getBroadcast(this, 0, button1I, 0);
rv.setOnClickPendingIntent(R.id.btn1,button1PI);
//修改自定义View中的图片(两种方法)
// rv.setImageViewResource(R.id.iv,R.mipmap.ic_launcher);
rv.setImageViewBitmap(R.id.iv, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContent(rv);
Notification notification = builder.build();
notificationManager.notify(num++, notification);
android内容提供者以及观察者
内容提供者
内容提供器作为Android四大组建之一.感觉没啥太大用途.
感觉就是一个应用程序创建一个可以被别的程序访问的数据库
访问其他程序中的数据
如果想要访问别的内容提供器中的共享数据,就要借助ContentResolver类,可以通过Context中的getContentResolver方法得到,它提供了一些列对数据的crud操作,操作和sqlitedatabase很相似,但是ContentResolver没有库和表,而是用一个uri代替,如’content://com.xxx.xxxx.xxx/aaa’表示访问包名是com.xxx.xxxx.xxx程序的aaa表,然后进行curd操作
查询:
1 | getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder); |
返回的是一个cursor结果集,和sqlite用法一样
- 查询手机上所有的联系人
1
2
3
4
5
6
7cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while(cursor.moveToNext()){
Log.d(TAG, "onCreate: "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
Log.d(TAG, "onCreate: "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
}
其中的参数都是系统预设的
查询手机上电话为10086的联系人的名字
1
2
3
4
5
6
7
8
9
10
11
12
13
14Cursor cursor = null;
try{
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},ContactsContract.CommonDataKinds.Phone.NUMBER+" = ?",new String[]{"10086"},null);
if(cursor!=null){
//找到了这个联系人
while(cursor.moveToNext()){
Log.d(TAG, "onCreate: "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
}
}else{
//没找到这个联系人
}
}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
41addContact("zhangphil", "12345678901","asdf@aa.com");
// 一个添加联系人信息的例子
public void addContact(String name, String phoneNumber,String email) {
// 创建一个空的ContentValues
ContentValues values = new ContentValues();
// 向RawContacts.CONTENT_URI空值插入,
// 先获取Android系统返回的rawContactId
// 后面要基于此id插入值
Uri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
// 内容类型
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
// 联系人名字
values.put(StructuredName.GIVEN_NAME, name);
// 向联系人URI添加联系人名字
getContentResolver().insert(Data.CONTENT_URI, values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
// 联系人的电话号码
values.put(Phone.NUMBER, phoneNumber);
// 电话类型
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
// 向联系人电话号码URI添加电话号码
getContentResolver().insert(Data.CONTENT_URI, values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
// 联系人的Email地址
values.put(Email.DATA, email);
// 电子邮件的类型
values.put(Email.TYPE, Email.TYPE_HOME);
// 向联系人Email URI添加Email数据
getContentResolver().insert(Data.CONTENT_URI, values);
}
内容观察者
内容观察者就是要观察一个uri,当这个uri下内容发生改变的时候执行的操作,类似于数据库的触发器
内容观察者要继承ContentObserver类,有一个handler参数用于多线程更新UI
主要方法是onChange(boolean self) 这个方法是当观察的uri发生改变的时候调用的方法
注册内容观察者
1 | getContentResolver().registerContentObserver(uri,notifyForDescendents,ContentResolver); |
uri是要监听的地址uri
- notifyForDescendents表示是否模糊匹配,例如一个uri为aa.bb.cc/dd,当notifyForDescendents为true的时候即模糊匹配,此时aa.bb.cc/ff发生变化时也会触发此观察者
为false时只有aa.bb.cc/dd发生变化时触发
ContentResolver是内容观察者对象
注销内容观察者
1 | getContentResolver().unregisterContentObserver(ContentResolver); |
创建内容观察者
1 | public class MyContentResolver extends ContentObserver { |
android的fragment(碎片)
OCTOBER 1, 2018
个人感觉碎片就是把activity分成一片一片的.和activity一样有生命周期
静态创建碎片
效果图:
整个界面分为上下两部分,是两个fragment
上面的fragment(title_fragment)
先创建布局
- fragment_title.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:background="@color/colorAccent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_back"
android:text="返回"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_title"
android:text="我是标题"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
创建titlefragment类
继承v4的Fragment1
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
31import android.support.v4.app.Fragment;
public class TitleFragment extends Fragment {
/**
* 表示fragment第一次创建绘制用户界面时系统回调的方法
* @param inflater 表示布局填充器或者加载器,将xml文件转化成view对象
* @param container 表示当前fragment出入activity的布局视图对象
* @param savedInstanceState 表示存储上一个fragment的信息
* @return view表示当前加载的fragment视图,如果fragment不 提供视图可以返回null
*/
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_title,container,false);
TextView tv1 = view.findViewById(R.id.tv_back);
tv1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getActivity(),"返回",Toast.LENGTH_SHORT).show();
}
});
TextView tv2 = view.findViewById(R.id.tv_title);
tv2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getActivity(),"标题",Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
重写了oncreateView方法,是第一次绘制用户界面调用的方法,里面使用布局填充器加载了xml布局,得到一个View对象,然后通过view.findviewbyid方法获取到布局上的空间,然后注册点击事件
下面的ContentFragment
xml布局
- fragment_content.xml
1
2
3
4
5
6
7
8
9
10
11
12
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="我是内容哈哈哈哈" />
</LinearLayout>
布局很简单,一个tv,一句话
同样创建他的Fragment类
- ContentFragment.java
1
2
3
4
5
6
7
8
9
10import android.support.v4.app.Fragment;
public class ContentFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_content,container,false);
return view;
}
}
activity加载fragment
- activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<fragment
android:id="@+id/fragment_title"
android:name="cn.xwmdream.fragment.TitleFragment"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp"/>
<fragment
android:id="@+id/fragment_content"
android:name="cn.xwmdream.fragment.ContentFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="9" />
</LinearLayout>
线性布局,上下两个fragment,name是要加载fragment的路径,注意,一定要给每一个fragment创建id,否则报错
MainActivity.java还是常规操作,不解释
动态引入fragment
在activity中
- 创建fragment的管理器对象
- 获取fragment的事务对象并开启(事务具有一致性)
- 调用事务的动态方法,动态添加移除替换fragment
- 提交事务
修改activity_main.xml
把原来的fragment换成linearlayout容器,这些容器一会动态添加fragment1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:orientation="horizontal"
android:id="@+id/ll_title"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"></LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/ll_content"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"></LinearLayout>
</LinearLayout>
- MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//1.创建fragment管理器对象
FragmentManager manager = getSupportFragmentManager();
//2.获取fragment的事务对象并且开启事务
FragmentTransaction transaction = manager.beginTransaction();
//3.调用事务操作fragment,
//add方法,第一个参数是把碎片添加到哪个布局,第二个是碎片对象
TitleFragment titleFragment = new TitleFragment();
transaction.add(R.id.ll_title,titleFragment);
transaction.add(R.id.ll_content,new ContentFragment());
//remove(Fragment)删除一个fragment对象,
//transaction.remove(titleFragment);
//replace(布局id,新的fragment对象) 把某个布局中的fragment替换成新的
//transaction.replace(R.id.ll_content,new TitleFragment());
//4.提交事务
transaction.commit();
注意动态引用的viewgroup里面不能有子元素
引用过add后如果不引用remove是不能再次add
replace相当于remove+add
生命周期
生命周期主要函数
onattach当碎片和活动建立起关联的时候调用
onCreateView()第一次为碎片创建视图(加载布局)的时候调用,控件关联写在这里
onActivityCreated()确保碎片与相关活动一定创建完毕的时候调用
onDestoryView当碎片与关联的试图移除的时候调用
onDetach当碎片和活动解除关联的时候调用
静态生命周期
(f代表fragment,a代表activity)
启动:f.onAttach->f.onCreate->f.onCreateView->a.onCreate->f.onActivityCreated->f.onStart->a.onStart->a.onResume->f.onResume
暂停:f.onPause->a.onPause->f.onStop->a.onStop
重新开始:a.onRestart->f.onStart->a.onStart->a.onResume->f.onResume
销毁:(暂停)->f.onDestroyView->f.onDestroy->f.onDetach->a.onDestroy
动态添加生命周期
在oncreate函数中添加:
a.onCreate>f.onAttach->f.onCreate->f.onCreateView->f.onActivityCreated->f.onStart->a.onStart->a.onResume->f.onResume
在事件中添加:
f.onAttach->f.onCreate->f.onCreateView->f.onActivityCreated->f.onStart->f.onResume
暂停,重新开始,销毁同上
心得:
感觉注册就是静态注册,看在哪个方法中注册,如果在系统回调函数中注册,会先打印activity回掉前的方法,和静态的相互顺序一样
fragment交互中的生命周期:
函数中的生命周期
add:当使用add函数启动另一个fragment,只执行第二个fragment的启动周期
此时的销毁周期f同时表示两个fragment的生命周期,并且老的fragment会先执行
replace:(1.表示原来的fragment,2表示新的fragment):2.onAttach->2.onCreate->1.onPause->1.onStop->1.onDestroyView->1.onDestroy->1.onDetach->2.onCreateView->2.onActivityCreated->2.onStart->2.onResume
remove:销毁中所有fragment的周期
返回栈中的生命周期:
压入栈:2.onAttach->2.onCreate->1.onPause->1.onStop->1.onDestroyView->2.onCreateView->2.onActivityCreated->2.onStart->2.onResume
类似replace,,区别:就是第一个销毁状态只执行到onDestroyView,不执行onDestroy和onDetach
弹出栈:(2表示栈顶对象,1表示底下对象):2.onPause->2.onStop->2.onDestroyView->2.onDestroy->2.onDetach->1.onCreateView->1.onActivityCreated->1.onActivityCreated->1.onResume
传值
activity向fragment传值:
activity通过id获取fragment对象:1
xFragment xfragment = (xFragment)getFragmentManager().findFragmentById(R.id.xfragment);
activity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//创建管理器
FragmentManager manager = getSupportFragmentManager();
//开启事务
FragmentTransaction transaction = manager.beginTransaction();
//创建碎片对象
TitleFragment1 titleFragment = new TitleFragment1();
//创建Bundle对象用于保存值
Bundle bundle = new Bundle();
//保存值
bundle.putString("hello","world");
//把值保存到fragment对象中
titleFragment.setArguments(bundle);
//添加
transaction.add(R.id.fl, titleFragment);
//提交
transaction.commit();fragment.java
1
2
3
4
5Bundle bundle = getArguments();
//不为空获取值
if(bundle!=null){
bundle.getString("hello");
}
fragment向activity传值
获取activity对象:
1 | getActivity(); |
传值思想:
创建一个接口,接口里面实现一个有参数值的方法,让activity实现这个接口,并重写接口那个方法
在fragment的onAttach方法中实例化那个接口,实例化对象就是getActivity
然后再需要传值的时候调用接口的那个方法就能把值传到activity重写的方法里
fragment向fragment传值
1、在AFragment中通过getFragmentManager.findFragmentById(int id)获取BFragment实例,调用BFragment的方法实现传值
2、在AFragment中通过getFragmentManager.findFragmentById(int id).getView().findViewById(int id)获取到BFragment中的view对象,对控件直接进行传值
3、在AFragment中直接getActivity().findViewById(int id)获取属于当前Activity的BFragment中的view对象
自适应(平板和手机的适配)
Android的限定符:
名称 | 像素密度范围 | 图片大小 |
---|---|---|
mdpi | 120dpi~160dpi | 48×48px |
hdpi | 160dpi~240dpi | 72×72px |
xhdpi | 240dpi~320dpi | 96×96px |
xxhdpi | 320dpi~480dpi | 144×144px |
xxxhdpi | 480dpi~640dpi | 192×192px |
small | 小屏幕 | |
normal | 基准屏幕 | |
large | 大屏幕 | |
xlarge | 超大屏幕 | |
land | 横向屏幕 | |
port | 纵向屏幕 | |
long | 比标准屏幕宽高比明显的高或者宽的这样屏幕 | |
notlong | 和标准屏幕配置一样的屏幕宽高比 |
可以创建不同屏幕的布局进行写一些逻辑.比如layout-xhdpi,表示像素密度在240dpi-320dpi使用的布局文件
在Java代码中通过fandviewbyid!=null判断是否加载某些不同布局的控件,写不同的逻辑
还可以使用最小宽度限定符,layout-sw600dp sw600dp表示small width 600dp,表示最小600dp时候使用的布局,及宽度大于等于600dp调用的布局.这里的宽度指的是长宽比较小的那个值
android滑动列表RecyclerView
简介
RecyclerView是Android官方推荐使用的滚动控件
我认为的recyclerview是通过一个布局实现整体的布局,然后通过一个适配器实现子项布局点击等一些操作
写一个水果列表.
首先添加依赖
1
'com.android.support:recyclerview-v7:28.0.0'
创建一个主activity,并编写布局,就是创建一个recyclerview,然后长宽match_parent
main_layout.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>创建一个水果列表的子布局,就是列表中单个元素的布局方式fruit_item.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>写一个水果列表子布局的类Fruit.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class Fruit {
private String name;
private int imageId;
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
}开始写适配器,并创建点击事件
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
56import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item,viewGroup,false);
final ViewHolder holder = new ViewHolder(view);
holder.fruitView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you click view "+fruit.getName(),Toast.LENGTH_SHORT ).show();
}
});
return holder;
}
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
Fruit fruit = mFruitList.get(i);
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
}
public int getItemCount() {
return mFruitList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitView = itemView;
fruitImage = (ImageView)itemView.findViewById(R.id.fruit_image);
fruitName = (TextView)itemView.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList){
mFruitList = fruitList;
}
}其中适配器实现了一个ViewHolder的内部类,里面是适配器对应的子控件,view是整个子控件,imageview是图片控件,textview是后面的文字控件,可以在适配器的onCreateViewHolder方法中为他们创建对应的点击事件.
mfruitList是对应整个滑动列表的元素列表
MainActivity.java,自备图片
MainActivity.java1
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
66import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
//水果元素列表
private List<Fruit> fruitList = new ArrayList<Fruit>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化水果列表
initFruits();
//绑定recycleview
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//加载一个布局
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//把布局弄成横向滑动的
//layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
//把布局弄成纵向3列瀑布流形式
//StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
//让recycleview加载创建的布局
recyclerView.setLayoutManager(layoutManager);
//创建适配器,传入水果元素列表
FruitAdapter adapter = new FruitAdapter(fruitList);
//加载适配器
recyclerView.setAdapter(adapter);
}
//初始化水果列表,传入水果名字和图片id
private void initFruits() {
for (int i = 0; i < 2; i++) {
fruitList.add(new Fruit("菠萝", R.drawable.boluo));
fruitList.add(new Fruit("草莓", R.drawable.caomei));
fruitList.add(new Fruit("橙子", R.drawable.chengzi));
fruitList.add(new Fruit("荔枝", R.drawable.lizhi));
fruitList.add(new Fruit("龙眼", R.drawable.longyan));
fruitList.add(new Fruit("芒果", R.drawable.mangguo));
fruitList.add(new Fruit("猕猴桃", R.drawable.mihoutao));
fruitList.add(new Fruit("苹果", R.drawable.pingguo));
fruitList.add(new Fruit("葡萄", R.drawable.puto));
fruitList.add(new Fruit("圣女果", R.drawable.shengnvguo));
fruitList.add(new Fruit("水蜜桃", R.drawable.shuimitao));
fruitList.add(new Fruit("提子", R.drawable.tizi));
fruitList.add(new Fruit("香蕉", R.drawable.xiangjiao));
fruitList.add(new Fruit("西瓜", R.drawable.xigua));
fruitList.add(new Fruit("西红柿", R.drawable.xihongshi));
fruitList.add(new Fruit("杨桃", R.drawable.yangtao));
fruitList.add(new Fruit("樱桃", R.drawable.yingtao));
}
}
}加载的时候要加载一个布局,上面写的是默认纵向列表布局,可以设置成横向,还有瀑布流形式,不过设置的时候一定要注意fruit_item.xml中间的布局变化
如果设置成横向滑动就不要把宽度设置成父类最大
如果设置成纵向瀑布流形式,要把最外层的width设置成父类最宽
更新数据
更新数据的方法是更新fruitList这个数组,然后再执行对应的适配器方法,可以把适配器弄成成员变量
刷新全部可见的item,notifyDataSetChanged()
刷新指定item,notifyItemChanged(int)
从指定位置开始刷新指定个item,notifyItemRangeChanged(int,int)
插入、移动或者删除一个并自动刷新,notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int)
局部刷新,notifyItemChanged(int, Object)
列表滚动到制定项
recyclerView.scrollToPosition(int);
会让第指定个项目出现在屏幕上,只是完全出现在屏幕上,不是屏幕第一个
高级用法
RecyclerView实现滑动删除和拖拽功能
总结和分析几种判断RecyclerView到达底部的方法
Android使用SharePreferences存储数据
SEPTEMBER 25, 2018
简介
SharedPreferences存储类用来存储一些键值对,比如用户的设置信息
可以存储五大基本类型String,Float,Long,int,boolean和字符串集合StringSet,其中StringSet是一个字符串的集合,Set
创建
1 | Set set = new HashSet<String>(); |
先创建一个sp对象,然后获取到编辑器editor,在editor中保存数据,保存完以后要进行commit,否则不能保存;
他会把数据保存到data/data/1
2
3
4
5
6
7
8
9
10
11
12
<map>
<string name="name">xwm</string>
<float name="float" value="3.1415925" />
<boolean name="bool" value="true" />
<long name="long" value="12" />
<set name="StringSet">
<string>one</string>
<string>two</string>
</set>
<int name="int" value="500" />
</map>
获取数据
1 | SharedPreferences spa = getSharedPreferences("data", MODE_PRIVATE); |
String: xwm
int500
booltrue
Long12
float3.1415925
StringSet[two, one]
先创建一个SharePreferences对象,然后可以通过get方法通过key获取到各类型的数据,get方法有两个参数,第一个是要取出值的key,第二个值是如果这个key没有对应的值要返回的默认值
取出方法还有一个getAll方法,是将所有的键值对弄成一个map返回回来
提交方法
sharepreferences有两个提交方法,一个是上面用到的commit,还有一个apply
这两个方法的区别在于:
- apply没有返回值而commit返回boolean表明修改是否提交成功
- apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
- apply方法不会提示任何失败的提示。
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。
参考:https://blog.csdn.net/jake9602/article/details/18414841
android广播
参考:Android四大组件:BroadcastReceiver史上最全面解析
安卓独有的广播机制,就像生活中的广播一样,一个发射广播,很多人能同时接受到广播内容
实现原理
采用的模型
- Android中的广播使用了设计模式中的观察者模式:基于消息的发布 / 订阅事件模型
- 因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展
模型讲解
- 模型中有3个角色:
- 消息订阅者(广播接收者)
- 消息发布者(广播发布者)
- 消息中心(AMS,即Activity Manager Service)
广播接收者
静态注册
- 静态注册是指在清单文件中注册
在as中new一个other选择Broadcast Receiver就能创建一个广播接收者,两个复选框,代表能不能由系统实例化和接收程序之外的广播
as会自动在清单文件注册,打开清单文件可以看到1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
- 注册实例
1
2
3
4
5
6
7
8<receiver
//此广播接收者类是mBroadcastReceiver
android:name=".mBroadcastReceiver" >
//用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
+action中的值是要接收的广播名字,就像现实广播中的’fm100.8’一样,是要接收广播的名字,当然可以是系统广播的名字,如android.intent.action.NEW_OUTGOING_CALL是系统打电话时发送的广播的名字
- 当此 App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
动态注册
动态注册是只在Java代码中进行注册,1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void onResume(){
TwoReceiver twoReceiver = new TwoReceiver();
//设置要广播接收的名字
IntentFilter intentFilter = new IntentFilter(android.net.conn.CONNECTIVITY_CHANGE);
registerReceiver(twoReceiver,intentFilter);
}
//记得要在onPause方法中释放注册的广播接收者
protected void onPause() {
super.onPause();
//释放注册的广播接收者
unregisterReceiver(twoReceiver);
}
- 动态广播最好在Activity 的 onResume()注册、onPause()注销。原因:
- 对于动态广播,有注册就必然得有注销,否则会导致内存泄露
- 在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。
- 广播接收者
重写了onReceive方法实现接收广播的逻辑
可以通过getResultData方法获取有序广播中的数据,通过setResult设置回数据1
2
3
4
5
public void onReceive(Context context, Intent intent) {
String result = getResultData();
Log.d("TwoReceiver","onReceivee: "+result);
}
发送广播
- 广播分为有序广播和无序广播
无序广播
一次发送,所有人同时接收
- 特点:
- 所有接收器没有先后顺序,接受顺序不确定,但是都能接受到
- 通过sendBroadcast(intent)方法来发送,它是完全异步的。
- 效率高
- 无法使用setResult系列、getResult系列及abort(中止)系列API
有序广播
一次发送,接收有顺序
特点:
- 有接收先后顺序,根据priority的值来判定先后顺序,值越大优先级越高
静态注册接收器:1
2
3
4
5
6
7
8<receiver
android:name=".OneReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="bmbm" />
</intent-filter>
</receiver>
动态注册接收器:1
2
3
4
5TwoReceiver twoReceiver = new TwoReceiver();
IntentFilter intentFilter = new IntentFilter("bmbm");
//设置代表优先级顺序的priority值
intentFilter.setPriority(1000);
registerReceiver(twoReceiver,intentFilter);
当两个接收者优先级一样时或者都没有优先级的时候,那么按照清单文件上注册的先后顺序先后收到广播
当一个静态注册和另一个动态注册的接受者优先级相同或者都没有优先级的时候,那么动态注册会先收到广播
通过sendOrderedBroadcast(intent,null);发送有序广播
可以使用setResult系列、getResult系列及abort(中止)系列API
当优先级高的接受者使用abortBroadcast()方法后,那么比他优先级低的广播接收者则接收不到广播
发送一个广播
1 | Intent intent = new Intent(); |
使用本地广播
本地广播,就是自己程序发的只有自己能收到,别的收不到
先创建广播接收器Myreceiver.java1
2
3
4
5
6
7public class MyReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "收到了", Toast.LENGTH_SHORT).show();
}
}
MainActivity.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver
mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//步骤2:实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步骤3:设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);
特别注意
- 对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
作者:Carson_Ho
链接:https://www.jianshu.com/p/ca3d87a4cdf3
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
android activity的生命周期和启动模式
SEPTEMBER 23, 2018
简介:
Android生命周期分为七个函数:onCreate,onStart,onResume,onPause,onStop,onRestart,onDestory
单个activity的生命周期
Android官方文档给的生命周期示例图:
1、Activity的启动 onCreate->onStart->onResume->处于可见状态
2、Activity的不可见 onPause->onStop
3、Activity的重新可见 onRestart->onStart->onResume
4、Activity的销毁 onPause->onStop->onDestory
一个Activity在创建和显示的时候,先调用的是onCreate, onStart, onResume,方法
按下back键的时候会调用:onPause,onStop,onDestory方法
按下home键会调用:onPause onStop
按下home键后再打开程序:onrestart onstart onresume
方法 | 说明 | 是否能事后终止? | 后接 |
---|---|---|---|
onCreate() | 首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(请参阅后文的保存 Activity 状态)。始终后接 onStart()。 | 否 | onStart() |
onRestart|在 Activity 已停止并即将再次启动前调用。始终后接 onStart()|否|onStart()
onStart()|在 Activity 即将对用户可见之前调用。
如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。|否|onResume()或onStop()
onResume()|在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。始终后接 onPause()。|否|onPause()
onPause()|当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。|是|onResume()或onStop()
onStop()|在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。|是|onRestart()或onDestroy()
onDestroy()|在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。|是|无
两个activity跳转时候的生命周期:
当第1个activity去访问第二个activity的时候会先调用
1.onPause->2.onCreate->2.onStart->2.onResume->1.onStop
当在第二个activity结束的时候会调用:
2.onPause->1.onRestart->1.onStart->onResume->2.onStop->2.Destory
注意
如果第二个activity类型是一个dialog类型的activity,那么第一个activity只会执行onPause,启动时候直接启动onResume,因为dialog类型的activity没有完全遮住下面的activity,所以只是暂停,并没有停止
规律
每次跳转到第二个activity的时候会先调用第一个activity的onpause,然后会调用即将显示activity的要显示的周期,然后再执行第一个activity的剩下的操作..原因是因为假设要访问的activity不能正确执行发生崩溃,能及时回到之前的activity,不至于之前界面的也被销毁了而执行不了,所以将之前的stop和destory方法第2个activity显示之后.
横竖屏的生命周期
基本生命周期:
竖.Pause->竖.Stop->竖.Destroy【竖屏销毁,并开始创建横屏】
横.Create->横.Start->横.Resume【横屏显示】
发生横竖屏切换的时候会先销毁之前的activity,然后重新创建横屏的activity,所以有一些数据要保存,此时可以重写onSaveInstanceState方法保存数据1
2
3
4
5
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("name","value");
}
然后在重写onRestoreInstanceState方法取出保存的数据1
2
3
4
5
6
7
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if(savedInstanceState!=null){
text.setText(savedInstanceState.getString("name"));
}
}
具体生命周期:
竖屏销毁时候:
onPause->onSaveInstanceState->onStop->onDestroy
横屏创建的时候
onCreate->onStart->onRestoreInstanceState->onResume
另外:
- 当重写了onSaveInstanceState发现,如果是要打开别的界面或者home暂停的时候也会调用该方法,此时如果返回此activity不会调用onRestoreInstanceState方法
如果是单击back返回,也就是界面要销毁的时候则不会调用. - 横屏可以重写布局,在res下新建一个layout-land的资源文件夹,里面创建同名的布局文件即可
- 我测试用的Android5.0手机,创建了个edittext,发现横屏的时候系统会给保存之前的数据,如果想要横屏修改这个数据,在oncreate中是不能修改的,必须在onRestoreInstanceState中修改,因为系统会在onRestoreInstanceState方法中把之前的数据写回来,,所以要在它之后再修改值才会生效.
- oncreate方法中也有个Bundle方法,也是保存之前的数据的,和onRestoreInstanceState中的用法一样
- 当然也可以设置禁止横屏,在清单文件中加入
android:screenOrientation=”portrait”
是可以让屏幕保持竖屏,不横屏,
如果是想保持横屏不竖屏,可以添加1
android:screenOrientation="landscape"
activity启动模式
Android的活动在内存里是以栈的形式存储
activity一共有四种启动模式:standard,,singleTop,,singleTask和singleInstance,可以通过activity标签指定android:launchMode属性来选择启动模式
standard:
standard是默认的启动模式,在不指定别的启动模式时,所有活动都会自动使用这种启动模式.
当有一个新的活动时,系统不会在乎此活动是否在堆栈中存在,每次启动都会为活动创建新的实例放入栈顶,
简单地说就是你启动一个活动,他就往栈顶放一个
singleTop
很多情况下第一种启动模式有些不合理,有的活动已经在栈顶了,为什么还要再次启动?
singleTop就解决了这个问题,当要启动的活动已经在栈顶时候(我觉得就是自己启动自己的时候),那么就不创建新的活动实例
singleTask
singleTop很好的解决重复创建栈顶活动的问题,但是如果要启动的活动已经在栈内,并没有在栈顶,还是会创建多个活动实例,singleTask是能让整个堆栈只存在一个相同的活动实例
比如说当前a活动是栈顶,b活动在a活动的下面,此时要启动b活动,那么会先将b活动弹出栈,此时使得a活动进入栈顶.
整个过程是要把a活动以上的活动都销毁(destroy),直到a活动处于栈顶为止
singleInstance
这个启动模式简单来说就是又创建了一个新的堆栈,是APP中LAUNCHER的activity所处在的栈(暂且叫他主栈)旁边又创建了一个堆栈(暂且叫他副栈)
第一个例子:有abc三个活动,比如b的启动模式是singleInstance,a启动了b,b启动了c,此时在c界面单击back,按理说应该是返回到b,其实是返回到a.因为b是生成到副栈,此时是两个堆栈,主栈是a->c,副栈里面有b,当在c中单击返回键,是会将c下面的a放到栈顶,如果再a中再次back,那么才会显示b,因为是先将主栈的显示,主栈都销毁了再启动副栈的活动
第二个例子:有ab两个活动,假设b的启动模式是singleInstance,在a中启动b,此时单击home键退出,然后再点进来,按理说应该还是显示b的界面,其实是显示的a,因为当重新启动了以后会先显示主栈的活动,没有的话才显示副栈
android弹出框dialog
内置dialog
普通对话框
1 | AlertDialog dialog = new AlertDialog.Builder(this).setTitle("我是标题").setMessage("我是内容").setIcon(R.mipmap.ic_launcher).setPositiveButton("确定", new DialogInterface.OnClickListener() { |
- 普通对话框可以设置标题,设置提示内容,以及设置确定和取消的点击事件
- 点旁边空白处也可以关闭dialog,但是不会调用取消的点击事件
单选对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17AlertDialog dialog = new AlertDialog.Builder(this).setTitle("我是标题").setIcon(R.mipmap.ic_launcher).setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "yes", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "no", Toast.LENGTH_SHORT).show();
}
}).setSingleChoiceItems(new String[]{"选项1", "选项2"}, 0, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, ""+which, Toast.LENGTH_SHORT).show();
}
}).create();
dialog.show();
- 如果设置了setMessage,那么就只显示内容,不会显示这个单选
- setSingleChoiceItems,第一个参数是一个字符串数组,第二个参数是默认选第几个,从0开始,第三个选项是一个点击事件,which是点击了第几个,从0开始
多选对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17AlertDialog dialog = new AlertDialog.Builder(this).setTitle("我是标题").setIcon(R.mipmap.ic_launcher).setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "yes", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "no", Toast.LENGTH_SHORT).show();
}
}).setMultiChoiceItems(new String[]{"选项1", "选项2"}, new boolean[]{true, false}, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Toast.makeText(MainActivity.this, ""+which+isChecked, Toast.LENGTH_SHORT).show();
}
}).create();
dialog.show();
- setMultiChoiceItems第一个参数是字符串数组,第二个参数是数组里的值默认选不选,第三个参数是点击事件,which是第几个选项发生了变化,第二个参数isChecked表示变化是点击了还是没点击
自定义对话框
先创建一个布局my_dialog.xml1
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
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</android.support.constraint.ConstraintLayout>
包含了上面一个textView和下面一个button
Mydialog.java1
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
37public class MyDialog extends Dialog {
private boolean isCreate;
private String message;
private TextView textView;
private Button button;
public MyDialog(Context context) {
super(context);
isCreate=false;
message=null;
}
public void setTextView(String text){
if(isCreate){
textView.setText(text);
}else{
message = text;
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_dialog);
isCreate = true;
textView = (TextView)findViewById(R.id.textView);
if(message!=null){
textView.setText(message);
}
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), "button", Toast.LENGTH_SHORT).show();
dismiss();
}
});
}
}
- 代码中重写了oncreate方法加载布局
- 当前对象如果还没有显示过,那么其内部子元素还没有关联资源(findviewbyid),此时如果调用一些方法,比如settext会报空值错误,参考setTextView的方法
- 给button设置了一个点击事件,显示一个”button”并且调用dismiss关闭当前弹出框
调用
MainActivity.java1
2
3MyDialog myDialog = new MyDialog(this);
myDialog.setTextView("你好啊");
myDialog.show();
dialog生命周期
Dialog的生命周期一共用以下6个方法:onCreate(),show(),onStart() ,cancel(),onDismiss(),Stop()
Dialog仅在在第一次启动时候会执行onCreate()方法(之后无论该Dialog执行Dismiss(),cancel(),stop(),Dialog都不会再执行onCreate()方法).
show() 和 onStart()在每次Dialog显示时都会依次执行.
onDismiss() 和 stop() 在每次Dialog消失的时候都会依次执行.
cancel() 是在点击BACK按钮或者Dialog外部时触发,然后依次执行onDismiss() 和 stop().
举例:
点击显示按钮,第一次显示Dialog,然后按BACK键返回
onCreate() —> onStart() —>show() ;
Stop() —> onDismiss() —> cancel();
再次点击显示按钮,然后点击Dialog外部.
onStart() —> show();
Stop() —> onDismiss() —> cancel();
再次点击显示按钮,然后执行Dialog.dismiss() 方法.
onStart() —> show();
Stop() —> onDismiss();
自定义dialog需要注意:
当前对象如果还没有显示过,那么其内部子元素还没有关联资源(findviewbyid),此时如果调用一些方法,比如settext会报空值错误
其他操作
dialog上的edittext获得交点并弹出键盘:
1 | /** |
- 然后在dialog外部调用的时候要先调用show,并且在多线程中调用此方法,如果不show就调用获得焦点会报错,
- 示例(runOnUiThread)
1
2
3
4
5
6
7
8
9
10
11
12
13myDialog.show();
//多线程调用弹出键盘操作
new Thread(){
public void run() {
runOnUiThread(new Runnable(){
public void run() {
myDialog.showNameKeyBoard();
}
});
}
}.start();
android操作sqllite的工具类
继承安卓自带的SQLiteOpenHelper,其中使用了代理模式(IResultSetUtil)操作结果集
需要注意的是,代理模式传入了cs(结果集对象)和db对象(连接对象),记得用完close.
sqllite中间传入的参数值都是字符串类型
创建工具类
SqlLiteHelper.java1
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
104import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SqlLiteHelper extends SQLiteOpenHelper {
private String firsql;
/**
* 构造方法
* @param context 上下文
* @param name 创建的文件名字,如database.db
* @param factory 不知道,父类要的,看样子像是结果集操作工厂
* @param version 定义的当前数据库的版本
* @param firsql 第一次运行执行的sql语句,一般是用来创建表
*/
public SqlLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, String firsql) {
super(context, name, factory, version);
this.firsql = firsql;
}
/**
* 创建时候调用的方法
* 只有在构造方法中的数据库名称那个文件不存在的时候才会调用这个方法
* @param db
*/
public void onCreate(SQLiteDatabase db) {
db.execSQL(firsql);
}
/**
* 当数据库版本变更的时候调用的方法,构造方法里的版本号
* @param db
* @param oldVersion
* @param newVersion
*/
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
/**
* 插入数据的方法
* @param tbname 插入数据的表名
* @param cols 要插入数据的列名
* @param values 要插入数据的值,和列名一一对应的值
*/
public void insert(String tbname, String[] cols, Object... values) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
for (int i = 0; i < cols.length && i < values.length; i++){
contentValues.put(cols[i], values[i].toString());
}
Long id = db.insert(tbname, null, contentValues);//返回插入数据的行号,与id无关
db.close();
}
/**
* 修改数据库的方法
* @param tablename 要修改数据表的表名
* @param cols 要修改数据的列名
* @param newvalue 修改后对应的值
* @param require 修改的条件,如'id=?'
* @param requirevalue 条件中问号对应的值
* @return 返回修改的行数
*/
public int update(String tablename, String[] cols, String[] newvalue, String require, String[] requirevalue) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
for (int i = 0; i < cols.length && i < newvalue.length; i++) {
values.put(cols[i], newvalue[i]);
}
int number = db.update(tablename, values, require, requirevalue);
db.close();
return number;
}
/**
* 删除数据
* @param tbname 要删除数据所在的表名
* @param require 删除的条件,如'id=?'
* @param values 条件中问号对应的值
* @return 返回删除的行数
*/
public int delete(String tbname,String require,String[] values){
SQLiteDatabase db = this.getWritableDatabase();
int number = db.delete(tbname,require,values);
db.close();
return number;
}
/**
* 执行传入的sql语句
* @param sql 要执行的语句
* @param iResultSetUtil 代理模式操作返回的数据库
* @param values 语句中问号对应的值
* @return 返回代理要的值
*/
public Object executeQuery(String sql,IResultSetUtil iResultSetUtil,String [] values){
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(sql,values);
return iResultSetUtil.doHandler(cursor,db);
}
}
IResultSetUtil.java1
2
3
4
5
6import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public interface IResultSetUtil {
public Object doHandler(Cursor cs, SQLiteDatabase db);
}
使用:
1 | //创建对象 |
事务:
事务分三步
1 | //开启事务: |
Android ADB命令大全
JANUARY 26, 2018
转自:https://www.jianshu.com/p/860bc2bf1a6a
作者:张明云
链接:https://www.jianshu.com/p/860bc2bf1a6a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
另外下载adb
ADB和Fastboot最新版的谷歌官方下载链接
ADB和Fastboot for Windows
https://dl.google.com/android/repository/platform-tools-latest-windows.zip
ADB和Fastboot for Mac
https://dl.google.com/android/repository/platform-tools-latest-darwin.zip
ADB和Fastboot for Linux
https://dl.google.com/android/repository/platform-tools-latest-linux.zip