Android笔记(九)使用ViewPager

使用 RecyclerView 可以实现动态加载数据。但如果我们有很多个页面,需要通过左右滑动来切换,就可以使用 ViewPager。

需要实现的效果:

阅读更多

Android 零碎知识

使用WebView

如果要在一个 Activity 上显示图片, 可以用 imageView + ScrollView 组合。但如果是长图片,其实还可以用 WebView

但是 WebView 有内存泄漏的风险,使用时要谨慎。

阅读更多

Android笔记(八)使用RecyclerView

Android笔记(二) Activity和布局 中,使用一个 String[] 模拟了一些数据,然后写入到一个 TextView 或者 ScrollView 中。

但这样有两个弊端:

  • ScrollView是一次性将内容绘制完毕,如果数据量很大,会导致内存消耗。
  • 无法通过点击 String[] 里面的某一个 String 进入详细页面

于是我们引入了 RecyclerView 。想象一下,我们平时刷微博、刷知乎,随着我们不断地向下刷,数据是动态加载出来的。 这就是RecyclerView。 当然,如果在 RecyclerView 里面嵌套 CardView 就能显示很丰富的内容了。

阅读更多

Android笔记(七)连接网络

当我们在知乎上面搜索“Android”的时候,可以看到地址栏的链接变化为:

https://www.zhihu.com/search?type=content&q=Android

其中,https://www.zhihu.com/search是 BASE_URL, 问号?后面的是参数。

这里的参数就是 type 为 content, q 为 Android 。

现在,我们要打造一个功能,用户在 EditText 上输入 Android , 我们的app 可以构造出 https://www.zhihu.com/search?type=content&q=Android 这样的 URL 出来。 并对该地址进行 HTTP 访问,然后获取 Response 结果。

以下将以 Github 的 API 为例

阅读更多

Android笔记(六) 运行时权限和内容提供器

什么是Content Provider ?

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享。它提供一套机制,允许在数据安全的条件下,让一个程序访问另一个程序的共享数据。比如,读取联系人app的电话号码数据等。

Content Provider可选择只对哪一部分数据进行共享,从而保证隐私。

但是在谈Content Provider之前,先来谈谈 Android 的运行时权限(Runtime permissions)


运行时权限(Runtime permissions)

Android 6.0 之后,Android 系统的权限分为两类:

  • 普通权限
  • 危险权限

普通权限在 AndroidManifest.xml 文件里直接声明,如下:

1
2
3
4
5
6
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jerrysheh.englishquickchecker">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
...
</manifest>

危险权限则需要使用运行时权限(runtime permssions)。Android 6.0 引入了这个概念,简单地说,就是在应用程序运行的时候,由用户手动授权是否调用手机的某些数据(比如,麦克风、相机、电话)。

关于普通权限和危险权限的区别,以及分别有哪些权限,可以参考 Google 官方文档

申请运行时权限

可以调用ContextCompat.checkSelfPermission()方法来检查是否有相应的权限,如果有返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果没有,返回 PERMISSION_DENIED,且应用必须明确向用户要求权限。

可以调用shouldShowRequestPermissionRationale()方法来向用户解释需要某些权限。如果用户第一次拒绝了权限,第二个需要这个权限的时候,该方法返回true。

可以调用requestPermissions()来申请权限。(此时系统会显示标准对话框让用户选择是否授权,我们无法更改)

可以重写onRequestPermissionsResult()方法来了解用户选择的结果。

开发模型:

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
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// 没有权限。
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
// 用户拒绝过这个权限了,应该提示用户,为什么需要这个权限。
} else {
// 申请授权。
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MMM);
}
}

...

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MMM: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被用户同意,可以去放肆了。
} else {
// 权限被用户拒绝了,洗洗睡吧。
}
return;
}
}
}

可以看到,上面的逻辑还是比较复杂的。而且,由于OEM厂商会在定制ROM上各种改,导致权限获取不正常,以致出现各种各样的 bug。我本人就因为在一加5T上测试上面的代码时,点击了拒绝权限,但没有任何的 Toast 提示,白白浪费了4个小时的时间研究。后来用 AOSP 测试通过了,才发现是一加改了系统的权限通知框。

解决上述问题,推荐开源库:AndPermission

项目地址: https://github.com/yanzhenjie/AndPermission

使用方法可参考:Android 6.0 运行时权限管理最佳实践


访问其他程序中的数据

未完待续

创建内容提供器

未完待续

Android开发中的一些坑

Gradle 加载慢的问题

第一次加载项目很慢一直显示Building “XXXX” Gradle project info

解决办法:

打开{your project}/gradle/wrapper/gradle-wrapper.properties

查看distributionUrl中 gradle 版本

https://services.gradle.org/distributions/ 下载相应版本的Gradle(官网地址:https://gradle.org/install)

放到以下目录即可

  • Linux:~/.gradle/wrapper/dists
  • Windows:C:\users\{user name}\.gradle\wrapper\dists

运行时权限在部分Android手机上无效

问题

按照 Google 文档的开发模型,写的运行时权限模型代码,在一加手机5T上,点击拒绝后没有任何提示,也就是说依然返回了有权限的STATUE_CODE,但是什么都没发生。

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
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// 没有权限。
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
// 用户拒绝过这个权限了,应该提示用户,为什么需要这个权限。
} else {
// 申请授权。
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MMM);
}
}

...

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MMM: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被用户同意,可以去放肆了。
} else {
// 权限被用户拒绝了,洗洗睡吧。
}
return;
}
}
}

解决办法

很多国产ROM都有这个坑。因此不要用上面的开发模型,推荐开源库:AndPermission

项目地址: https://github.com/yanzhenjie/AndPermission

使用方法可参考:Android 6.0 运行时权限管理最佳实践


Android Device Monitor 打不开的问题

查看Log:C:\Users\Jerrysheh\AppData\Local\Android\Sdk\tools\lib\monitor-x86_64\configuration\1520661867795.log

关键报错

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
!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.224
!MESSAGE Bundle reference:file:org.apache.ant_1.8.3.v201301120609/@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.255
!MESSAGE Bundle reference:file:org.apache.jasper.glassfish_2.2.2.v201205150955.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.255
!MESSAGE Bundle reference:file:org.apache.lucene.core_2.9.1.v201101211721.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.458
!MESSAGE Bundle reference:file:org.eclipse.help.base_3.6.101.v201302041200.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.474
!MESSAGE Bundle reference:file:org.eclipse.help.ui_3.5.201.v20130108-092756.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.474
!MESSAGE Bundle reference:file:org.eclipse.help.webapp_3.6.101.v20130116-182509.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.474
!MESSAGE Bundle reference:file:org.eclipse.jetty.server_8.1.3.v20120522.jar@4 not found.

!ENTRY org.eclipse.osgi 4 0 2018-03-10 14:04:28.521
!MESSAGE Bundle reference:file:org.eclipse.ui.intro_3.4.200.v20120521-2344.jar@4 not found.

java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:74)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:353)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:180)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:629)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:584)
at org.eclipse.equinox.launcher.Main.run(Main.java:1438)
at org.eclipse.equinox.launcher.Main.main(Main.java:1414)

解决办法

卸载 JRE 9。 (JDK 9 可以不用卸载) , 然后装 JDK 8 。

然后启动 Android Studio 时,使用管理员权限打开。

Android笔记(五)持久化技术

什么是持久化技术

持久化技术指的是将内存中产生的瞬时数据保存到存储设备中,这样,当手机关机再重启,这些数据不会丢失。Android的持久化技术提供了一套机制,让数据在瞬时状态和持久状态之间进行转换。

Android 主要提供了 3 种数据持久化功能,分别是:

  • 文件存储:顾名思义,用于保存文本或二进制数据等文件
  • SharedPreference存储:保存相对较小的键值集合
  • 数据库存储:将数据保存到数据库

文件存储

File 对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。 例如,它适合于图片文件或通过网络交换的任何内容。

早期的Android设备,由于内置存储空间非常有限(2011年买的Samung Galaxy S+只有 8 G 存储空间),因此通常都会外置SD卡。所以,Android的存储分为内部和外部。也有一些设备,虽然不支持SD卡,但依然人为地把存储空间分为外部和内部。一般来说,我们推荐把文件保存在内置存储当中,因为它始终可用,且只有自己的应用才能访问此处保存的文件,当自己的应用被卸载时,这些文件也会被移除。

如果要将文件写入外部存储中,请参考官方文档

将文件保存在内部存储中

无需任何权限,即可在内部存储中保存文件。

  • 可以用 getFilesDir() 方法返回表示应用的内部目录的 File 。 用getCacheDir()方法返回表示应用临时缓存文件的内部目录的 File。

关于写入文件和写入缓存,请参考官方文档

保存文本示例

首先定义一个 EditText

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">

<EditText
android:id="@+id/edit_text_name_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="input your name"
android:padding="4dp"
android:textSize="24sp" />

</LinearLayout>

然后重写onCreate方法和onDestroy方法

Context类提供了一个 FileOutputStream 类型的 openFileOutput()方法,用于输出数据到文件。

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
public class MainActivity extends AppCompatActivity {
private EditText edit;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// edit 实例
edit = findViewById(R.id.edit_text_name_input);

}

@Override
protected void onDestroy(){
super.onDestroy();

// 获取 edit 的内容
String inputText = edit.getText().toString();
save(inputText);
}

public void save(String inputText){
FileOutputStream outputStream;

try {
// 输出流
outputStream = openFileOutput("myName", Context.MODE_PRIVATE);
outputStream.write(inputText.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

这样当我们退出app,edittext的文本就会被自动保存起来。

从保存的文件中读取数据

类似于输出流,Context类也提供了一个 FileinputStream 类型的 openFileinput()方法,用于输出数据到文件。

openFileinput(filename)的参数是文件名,一旦指定,系统会自动从 /data/data//files/ 目录下加载这个文件,并返回一个 FileinputStream 对象。

一个完整的示例

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
public class MainActivity extends AppCompatActivity {

private EditText dataEditText;
private String inputText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataEditText = findViewById(R.id.editText);
inputText = load();

// TextUtils.isEmpty方法:当 inputText 为 null 或 空 时返回 true
if (!TextUtils.isEmpty(inputText)){
dataEditText.setText(inputText);
dataEditText.setSelection(inputText.length());
Toast.makeText(this, "恢复数据成功", Toast.LENGTH_SHORT).show();
}

}

// 从文件加载数据
private String load() {
StringBuilder content = new StringBuilder();
String line;

try (
FileInputStream fileInputStream = openFileInput("data");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream))
){
while( (line = bufferedReader.readLine()) != null ){
content.append(line);
}
} catch (IOException IOe){
IOe.printStackTrace();
}
return content.toString();
}

@Override
protected void onDestroy() {
super.onDestroy();
String inputText = dataEditText.getText().toString();
saveText(inputText);
}

//保存数据到文件
private void saveText(String inputText) {
try(
FileOutputStream outputStream = openFileOutput("data",Context.MODE_PRIVATE);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))
){
bufferedWriter.write(inputText);
} catch ( IOException e){
e.printStackTrace();
}
}
}

思路:

  • onCreate()方法中调用 load() 来读取文件中的数据
  • onDestroy()方法中调用 saveText() 来保存数据到文件

SharedPreference存储

文件存储还是比较麻烦的,SharedPreference可以用键值对的方式来存储数据。

  • 保存数据时,给数据提供一个键
  • 读取数据时,根据键找到值

在 Android 中,有三种方法得到 SharedPreference 对象:

  • Context 类中的 getSharePreference(filename, mode)方法

参数1是存储的文件名,目录在/data/data/<package name>/share_prefs/ ,参数2是模式,默认 MODE_PRIVATE

  • Activity 类中的 getPreferences(mode) 方法

只有一个mode参数,因为这个方法会把当前类名作为 filename

  • PreferenceManager 类中的 static getDefaultSharedPreferences(Context)方法

Android笔记(四) Broadcast

什么是 Broadcast Receiver

广播接收器(Broadcast Receiver)允许你的应用接收来自各处的广播消息,比如开机广播,电池电量变化广播,时间或地区变化广播,以及来自电话、短信和其他app发出的广播消息等等。同样,我们的应用也可以向外发出广播消息。

Android中的广播可以分为以下两种:

  • 标准广播(Normal Broadcast),一种完全异步执行的广播,当广播发出后,所有的广播接收器几乎同一时刻接收到这条广播信息,没有先后顺序之分。这种广播效率高,但无法截断。

  • 有序广播(Ordered Broadcast),一种同步执行的广播,当广播发出后,同一时刻只有一个广播接收器收到这条广播,当这个广播接收器的逻辑执行完毕后,广播才会继续传递。这样一来,优先级高的广播接收器可以先收到广播,并且前面的广播接收器可以截断正在传递的广播。

阅读更多

Android笔记(三) Intent 和 Activity的生命周期、启动模式

Intent

Intent 是一个消息传递对象。它是 Android 程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。

关键词: 指明要执行的动作传递数据

Intent 的基本使用场景

  • 启动 Activity
    Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。

    如果希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在 Activity 的 onActivityResult() 回调中,您的 Activity 将结果作为单独的 Intent 对象接收。

  • 启动服务
    Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。

    如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),您可以从其他组件绑定到此服务。

  • 传递广播
    广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以将广播传递给其他应用。

参阅:官方文档 - Intent 和 Intent 过滤器

使用显式Intent

显式 Intent 指的是明确地按名称(完全限定类名)指定要启动的组件。比如说,如果我们想在FirstActivity这个活动中打开SecondActivity,我们可以在FirstActivity中的一个按钮点击中调用StartActivity,传入intent对象。

1
2
3
4
5
6
7
8
Button buttonIntent = (Button) findViewById(R.id.button_intent);
buttonIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
});
  • 定义一个按钮
  • 在按钮点击事件中 new 一个 intent 对象
  • 调用 startActivity,传入 intent 对象

使用隐式Intent

隐式 Intent 不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。 例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。 如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并向其传递 Intent 对象。 如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

在AndroidManifest.xml中,把SecondActivity段修改如下

1
2
3
4
5
6
7
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.jerrysheh.hello.ACTION_START" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.jerrysheh.hello.MY_CATEGORY"/>
</intent-filter>
</activity>

在<intent-filter>中增添了actioncategory段,只有 action 和 category 同时匹配才能响应该Intent。每个Intent中只能指定一个action,但能指定多个category。

  • action:声明接受的 Intent 操作。
  • category:声明接受的 Intent 类别。

除了actioncategory外,还有一个data,请参考官方文档

修改按钮点击事件, new Intent对象,因为我们想启动能到响应为com.jerrysheh.hello.ACTION_START这个action的活动,因此参数填入com.jerrysheh.hello.ACTION_START

1
2
3
4
5
6
7
8
9
Button buttonIntent = (Button) findViewById(R.id.button_intent);
buttonIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.jerrysheh.hello.ACTION_START");
intent.addCategory("com.jerrysheh.hello.MY_CATEGORY");
startActivity(intent);
}
});

或者,可以这样写,利用intent.setAction方法。

1
2
3
4
5
6
7
8
9
10
Button buttonIntent = (Button) findViewById(R.id.button_intent);
buttonIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction("com.jerrysheh.hello.ACTION_START");
intent.addCategory("com.jerrysheh.hello.MY_CATEGORY");
startActivity(intent);
}
});

如果intent.addCategory指定的Category没有一个活动能够匹配,那么程序会抛出异常。稍作修改,用resolveActivity()方法来判断是否有应用能响应。假设没有活动匹配,就不启动startActivity();

1
2
3
4
5
6
7
8
9
10
11
12
13
Button buttonIntent = (Button) findViewById(R.id.button_intent);
buttonIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.jerrysheh.hello.ACTION_START");
intent.addCategory("com.jerrysheh.hello.MY_CATEGORY");

if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}

}
});

Intent的更多用法

调用浏览器和拨号

new 一个 Intnet 对象后, 用 intent.setData(uri) 方法可以调用其他程序

比如调用浏览器打开 github

1
2
3
Intent intent = new Intent("com.jerrysheh.hello.ACTION_START");
intent.setData(Uri.parse("https://www.github.com"));
startActivity(intent);

调用系统拨号拨打10010

1
2
3
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10010"));
startActivity(intent);

可以在 AndroidManifest 的 <intent - filter>标签中配置 标签, 指定当前活动可以响应什么类型的数据。这样其他app响应这种数据的时候,Android系统会弹出选项,你的app会在可选列表里面

1
2
3
4
5
6
7
8
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.jerrysheh.hello.ACTION_START" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:schme="https" />
<data android:host="www.zhihu.com" />
</intent-filter>
</activity>

这样, 你的app可以响应知乎网站的浏览器调用


使用Intent传递数据

向下一个活动传递数据

可以用 intent 的 putExtra 方法向下一个活动传递数据。核心思想是,把数据存在String里,通过intent参数传递给下一个活动,下一个活动启动后从intent取出。

存放 (MainActivity.java)

1
2
3
4
String data = "this is data"
Intent intent = new Intent("com.jerrysheh.hello.ACTION_START");
intent.putExtra(Intent.EXTRA_TEXT, data);
startActivity(intent);

取出 (SecondActivity.java)

1
2
Intent intent = getIntent();
String data = intent.getStringExtra(Intent.EXTRA_TEXT);

返回数据给上一个活动

Activity中有一个StartActivityForResult()方法用于启动一个活动,但期望活动销毁后(通常是按下返回键或调用finish()方法)返回一个结果给上一个活动。

MainActivity.java

1
2
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
StartActivityForResult(intent, 1);

SecondActivity.java

1
2
3
4
Intent intent = new Intent();
intent.putExtra("data_return", "this is back data");
setResult(RESULT_OK, intent);
finish();

当然,返回数据后,会回调MainActivity的onActivityResult()方法,因此我们还需要重写这个方法拿到SecondActivity返回来的数据。

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case 1:
if (requestCode == RESULT_OK) {
String returnData = data.getStringExtra("data_return");
...
}
break;
}
}

使用 intent 传递对象

上面的 intent 只能传 String, 如果我们有一个 javabean 对象需要传递,怎么做呢?

首先将实体类(bean)实现 Serializable 接口。注意: 如果 bean 里面嵌套了 bean,内部类也要声明为实现 Serializable 接口。

1
2
3
4
5
6
7
8
9
public class bean implements Serializable {
int a;
int b;
String c;
Heybean d;
public static class Heybean implements Serializable {
...
}
}

传递 activity

1
2
3
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra("name",detailbean);
context.startActivity(intent);

接收 activity

1
Bean detailBean = (Bean) getIntent().getSerializableExtra("name");

Activity的生命周期

Android 用 任务(Task)来管理活动,一个Task就是一组存放在返回栈(Back Stack)里的活动的集合。系统总是会显示处于栈顶的活动给用户。

Activity的四种状态

  • 运行状态
  • 暂停状态(弹出式卡片,背景活动就是暂停状态)
  • 停止状态
  • 销毁状态

Activity的生存期

Toast

  • 完整生存期
    活动在onCraete()onDestroy()之间经历的,就是一个完整生存期。

  • 可见生存期
    活动在onStart()onStop()之间经历的,就是一个可见生存期。onStart()方法在活动从不可见变为可见时调用,onStop()反之。

  • 前台生存期
    活动在onResume()onPause()之间经历的,就是一个前台生存期。onResume()方法在活动准备好和用户交互时调用。当系统准备去启动或恢复另一个活动时,onPause()将当前活动一些消耗CPU的资源释放,同时保存关键数据。

此外,还有一个onRestart(),用于活动从停止状态变为运行状态之前调用,也就是活动被重新启动。

当系统内存不足时,用户按下back键返回到上一个Activity,有可能上一个Activity已经被系统回收,这时不会执行onRestart(),而是执行onCreate()。遇到这种情况,如果上一个Activity有数据,那这些数据都丢失了,这是很影响用户体验的。解决办法是调用onSaveInstantState()回调方法。具体参见《第一行代码》第二版p62,以及Activity Google官方文档(推荐)

Activity的启动模式

Activity有四种启动模式,可以在 AndroidManifest.xml 的 标签中修改

1
2
3
4
5
6
7
8
<activity
android:name=".SecondActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="com.jerrysheh.hello.ACTION_START" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

android:launchMode可填以下四种模式

  • standard

    标准模式,在MainActivity中启动MainActivity,会重复创建MainActivity的新实例。如创建了3个MainActivity的实例,需要按3次返回键才能完全退出。

  • singleTop

    如果MainActivity已经在栈顶,启动MainActivity则不会重复创建新实例。但MainActivity不在栈顶,还是会创建新实例。

  • singleTask

    无论MainActivity是否在栈顶,在整个应用程序上下文中只存在一个MainActivity实例。

  • singleInstance

    单独创建一个返回栈存放该实例。解决不同应用共享一个返回栈的问题。

参考:Google官方文档

Android笔记(二) Activity和布局

Activity

Activity 是用户可以执行的单一任务。负责创建新的窗口,供应用绘制和从系统中接收事件。

Activity 是用 Java 编写的,扩展自 Activity 类。

Activity 会创建视图来向用户显示信息,并使用户与 Activity 互动。视图是 Android UI 框架中的类。它们占据了屏幕上的方形区域,负责绘制并处理事件。Activity 通过读取 XML 布局文件确定要创建哪些视图(并放在何处)。这些 XML 文件存储在标记为 layouts 的 res 文件夹内。

参阅Activity Google官方文档


布局 XML

Android 项目中的布局在 res/layouts 目录下的 XML 文件编写。

示例

1
2
3
4
5
6
7
8
9
10
11
<TextView
android:id="@+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
阅读更多

Android笔记(一) 初识

Android 8

Android 简介

Android,常见的非官方中文名称为安卓,是一个基于Linux内核的开放源代码移动操作系统,由Google成立的Open Handset Alliance(OHA,开放手持设备联盟)持续领导与开发,主要设计用于触屏移动设备如智能手机和平板电脑与其他便携式设备。

Android最初由安迪·鲁宾等人开发制作,最初开发这个系统的目的是创建一个数码相机的先进操作系统;但是后来发现市场需求不够大,加上智能手机市场快速成长,于是Android成为一款面向智能手机的操作系统。

于2005年7月11日被美国科技企业Google收购。

2007年11月,Google与84家硬件制造商、软件开发商及电信营运商成立开放手持设备联盟来共同研发改良Android,随后,Google以Apache免费开放源代码许可证的授权方式,发布了Android的源代码(Android OpenSource Projet),开放源代码加速了Android普及,让生产商推出搭载Android的智能手机,Android后来更逐渐拓展到平板电脑及其他领域上。

2010年末数据显示,仅正式推出两年的Android操作系统在市场占有率上已经超越称霸逾十年的诺基亚Symbian系统,成为全球第一大智能手机操作系统。

在2014年Google I/O开发者大会上Google宣布过去30天里有10亿台活跃的安卓设备,相较于2013年6月则是5.38亿。

2017年3月,Android全球网络流量和设备超越Microsoft Windows,正式成为全球第一大操作系统。

2017年8月,Android O发布。

以上简介来自Wikipedia,可见 Android 前景还是非常大的。作为一个移动开发者,学习Android也是势在必行了。

本次Android的学习,我采用书本+视频的方式。

参考书籍是:

参考视频:

对于没有编程基础的:

有编程基础可以直接从这个开始:

阅读更多
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×