SpringMVC与安卓之间的交互

接着上一篇 springMVC与jsp的交互,今天有空就测试了一下与安卓的交互
其实安卓与servlet交互和安卓与SpringMVC的交互一样就是改变一下,目前我用的是get方法访问SpringMVC,由于服务器程序沿用的是与上次jsp一样的代码,所以间接的进行了一次数据交互,把jsp的页面代码传到安卓上面来了
环境声明 :服务器环境与上一篇一样,安卓使用的是 Android studio 2.3.3

1、主要代码解释 #
A 服务器端程序 ##
package com.springmvc.test1;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import com.springmvc.pojo.RoleParams;

@Controller
@RequestMapping(“/tsetClass”)
public class SpringMVCTest1 {

@RequestMapping(“/testParams”)
public ModelAndView testParams(String roleName,String note) throws Exception{

System.out.println(“roleName===” + roleName);
System.out.println(“note===” + note);
ModelAndView mv = new ModelAndView();
mv.setViewName(“t2params”);//请看下面的图片
return mv;
}

}

mv.setViewName(“t2params”);就是把jsp中的内容返回了给安卓APP,角色名称、备注都只是个标识,在介绍安卓APP的时候就会发现APP上有同样的字眼

B 安卓APP程序 ##
package com.hemycoo.server.androidserverv1;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.hemycoo.server.androidserverv1.utils.HttpUtils;
import com.hemycoo.sever.javabean.Test;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

private EditText ed_number;
private EditText ed_pwd;
private TextView tv_status;

String path;

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

ed_number = (EditText) findViewById(R.id.ed_number);
ed_pwd = (EditText) findViewById(R.id.ed_pwd);
tv_status = (TextView) findViewById(R.id.login_status);

}

public void login(View v) throws IOException {

// 获得 QQ号码和 密码 , 然后登录
final String number = ed_number.getText().toString().trim();
final String pwd = ed_pwd.getText().toString().trim();

if (TextUtils.isEmpty(number) || TextUtils.isEmpty(pwd)) {
Toast.makeText(this, “号码或者密码不能为空”, Toast.LENGTH_SHORT).show();
return;
}
//使用okhtttp连接服务器
path = “http://10.149.72.101:8080/SpringMVC1024/tsetClass/testParams.action?roleName=”+number+”&”+”note=”+pwd;
//其中10.149.72.101为你本机的ip
HttpUtils.sendHttpRequest(path, new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
//Toast.makeText(MainActivity.this, “连接错误”,Toast.LENGTH_SHORT).show();
Log.e(“okhttpOnFailer”, “连接失败”);
}

@Override
public void onResponse(Call call, Response response) throws IOException {

String responseData = response.body().string();
if(responseData == null){
tv_status.setText(“null”);
return;
}

showResponse(responseData);
}
});

}

private void showResponse(final String response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// 在这里进行UI操作,将结果显示到界面上
tv_status.setText(response);
}
});
}
}

其中okhttp的使用才是联网的重点,下面我先把我的工程目录截图,再粘贴okhttp联网工具我封装的代码

package com.hemycoo.server.androidserverv1.utils;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;

public class HttpUtils {
public static final MediaType JSON= MediaType.parse(“application/json; charset=utf-8”);

public static void sendHttpRequest(String address,okhttp3.Callback callback){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.build();
client.newCall(request).enqueue(callback);
}

public static void sendHttpRequestPost(String path, String json, okhttp3.Callback callback) {
OkHttpClient client = new OkHttpClient();

RequestBody requestBody = RequestBody.create(JSON ,json);

Request request = new Request.Builder()
.url(path)
.post(requestBody)
.build();
client.newCall(request).enqueue(callback);
}

}

我用的okhttp不是*新的,如有需要可以百度一下或者谷歌一下在github上会有新版本的介绍

2 效果显示 #
A 首先是服务器端 ##

前面两个是网址测试时候打印的,圈出来的是APP提交的。

B APP界面 ##

可以看出来账号密码就是提交给SpringMVC的,而下面的内容就是jsp的内容,就是服务器返回的
至此呢springMVC与前端的交互就完成了,虽然是基本的,但也足够了,别的只不过是在这个基础这个原理上的延伸。
与SpringMVC交互就是与SSM交互,都是一样的,这些都是简单的数据交互,与前端*大的困难其实就是数据交互,接下来遇到的新的数据交互问题在有空的时候再进行更新吧,写这篇主要是给初学者看的,毕竟培训机构也没有讲SpringMVC之类的与安卓的交互,网上的内容也不太多,我的程序在我的电脑上是完全能够运行的,看到这篇文章还有疑问的欢迎评论交流

Spring For Android初步

Spring For Android初步
Spring for Android 支持在Android环境下使用Spring框架,其意在简化Android本地开发。这包括能够在你的Android应用程序的Rest客户端使用RestTemplate。除了本次主要讲解的网络请求部分,Spring for Android还提供了基于OAuth支持将Spring Social的功能集成到Android应用程序, 授权客户机和实现流行的社交网站,如Twitter和Facebook。

RestTemplate Module

Spring的RestTemplate是一个健壮的、流行的基于java的REST client。Spring Android的RestTemplate模块提供一个能够在Android环境下工作的RestTemplate版本。

RestTemplate类是SFA RestTemplate组件包的核心类。他在概念上类似Spring框架其他同级项目中提供的模板类。RestTemlpate的行为表现取决于他提供的那些回调方法,同时通过配置合适的HttpMessageConverter 类,用户可以高效的将对象转换成HTTP请求中的信息或从响应消息中将信息转换回对应的对象。

每当创建一个新的RestTemplate实例,构造器就会生成配套的辅助对象用于支持RestTemplate的功能。以下是RestTemplate组件中一些提供支持的模块介绍。

HTTP Client

RestTemplate 提供了抽象的RESTful HTTP 请求的模块,内部实现中,RestTemplate 封装了原生的Android HTTP 客户端连接包。这包括标准的J2SE连接组件(用SimpleClientHttpRequestFactory封装)和HttpComponents HttpClient组件(用HttpComponentsClientHttpRequestFactory封装)。默认情况下具体使用哪种ClientHttpRequestFactory 取决于Android的版本。

@SuppressWarnings(“deprecation”)
protected HttpAccessor() {
if (httpClient43Present) {
this.requestFactory = new HttpComponentsClientHttpRequestFactory();
}
else if (okHttpPresent) {
this.requestFactory = new OkHttpClientHttpRequestFactory();
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
this.requestFactory = new SimpleClientHttpRequestFactory();
}
else {
this.requestFactory = new org.springframework.http.client.HttpComponentsAndroidClientHttpRequestFactory();
}
}
(2.3之后建议使用标准的J2SE连接组件)

 

Gzip压缩器

HTTP规范中允许通过在请求头中加入Accept-Encoding参数指定传输字节流的压缩方式。目前为止,RestTemplate 可以通过Gzip压缩组件支持在发送和接受时通过gzip(仅支持gzip)格式压缩过的数据。

对象-Json转换

如果需要支持这种转换,SFA的RestTemplate需要第三方Json数据映射包的支持。目前SFA提供三种包支持:Jackson JSON Processor, Jackson 2.x, 和 Google Gson。虽然Jackson 系列是有名的JSON解析包,但Gson的包更小,更适合生成小型的Android程序安装包。
SFA 将支持代码放在包 org.springframework.http.converter.json中。

RSS和Atom 摘要文件支持

RSS和Atom的支持也需要第三方包, Android ROME Feed Reader包就提供了相应的功能。

SFA 将支持代码放在包org.springframework.http.converter.feed 中。

所需要的包文件

spring-android-rest-template-{version}.jar

spring-android-core-{version}.jar

 

RestTemplate 构造器

下面列出四种RestTemplate 的构造方法。默认的构造方法不包含消息的转换器,因此必须自己添加消息转换器。

如上文所讲,如果需要一个特别ClientHttpRequestFactory ,也可以向构造器中传入一个ClientHttpRequestFactory 的实例参数。

RestTemplate();

RestTemplate(boolean includeDefaultConverters);

RestTemplate(ClientHttpRequestFactory requestFactory);

RestTemplate(boolean includeDefaultConverters, ClientHttpRequestFactory requestFactory);

 

RestTemplate API简介

RestTemplate 对应主要的六种Http 访问方法封装了6类高层调用方法,通过他们可以非常轻松且高效的完成对RESTful 服务的服务请求。

RestTemplate 的API方法命名遵循同样的规则:  method+return

比如getForObject() 将调用HTTP GET方法,同时将HTTP响应数据转换成你指定的对象类型并返回。而postForLocation() 将调用HTTP POST方法,将给定的对象转换成HTTP请求内容发送,返回的信息中将标识新的对象资源所在的URL。

当HTTP请求发生异常时,异常信息将被封装在RestClientException 中并被抛出,可以通过在RestTemplate中实现ResponseErrorHandler来捕获并处理。

restTemplate.setErrorHandler(errorHandler);

/*捕获http请求发生异常RestClientException*/

restTemplate.setErrorHandler(new ResponseErrorHandler() {

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
//获得请求响应,通过判断执行不同的操作。。。
return false;//返回true表示有错误,false表示没有错误
}

@Override
public void handleError(ClientHttpResponse response) throws IOException {
//处理相应的错误操作。。。
//该方法只有在hasError方法中返回true时才会执行
}
});
网络请求方法(其他请求方式类似,可参考官方在线的API文档,链接在参考资料中):

HTTP GET

public <T> T getForObject(String url, Class<T> responseType, Object… urlVariables) throws RestClientException;

public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException;

public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException;

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object… urlVariables);

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables);

public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;

 

HTTP POST

public URI postForLocation(String url, Object request, Object… urlVariables) throws RestClientException;

public URI postForLocation(String url, Object request, Map<String, ?> urlVariables);

public URI postForLocation(URI url, Object request) throws RestClientException;

public <T> T postForObject(String url, Object request, Class<T> responseType, Object… uriVariables);

public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);

public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;

public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object… uriVariables);

public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;

HTTP 消息转换器

对象通过getForObject(), getForEntity(), postForLocation(), postForEntity(), postForObject() and put() 发送或者返回消息时都是通过HttpMessageConverter 的具体实现,从而转换成Http请求或者从Http应答中转换。从下面的HttpMessageConverter  API我们能对他的功能有个更深的认识。

public interface HttpMessageConverter<T> {
//判断给定的类型是否可以被转换器读取
//clazz 用于测试是否能够读取
//mediaType 读取的网络媒介类型,如果不指定,可以为null
//如果返回true表示可读,false表示其他情况
boolean canRead(Class<?> clazz, MediaType mediaType);
// 判断给定的类型是否可以被转换器写入
//clazz 用于测试是否能够写入
//mediaType 写入的网络媒介类型,如果不指定,可以为null
//如果返回true表示可写,false表示其他情况
boolean canWrite(Class<?> clazz, MediaType mediaType);

// 返回可被该转换器支持的header头消息类型
List<MediaType> getSupportedMediaTypes();

// 将给定的inputMessage消息转换成相应的类型对象并返回
// clazz 返回的类型,这个类型必须预先通过canRead方法,返回true
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;

// 将给定的对象转换后写入outputMessage
// t 写入outputMessage的对象,这个对象的类型必须预先通过canWrite方法,并返回true
// contentType  写入时的媒介类型,如果为null的话,则必须使用默认的内容类型转换器,否则,媒介类型必须预先通过canWrite方法且返回值为true
// outputMessage  要写入的消息
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
其中,SFA框架已经提供了现成的MediaType(即mime类型)。

部分类型:

{ “.323”, “text/h323” },

{ “.3gp”, “video/3gpp” },

{ “.aab”, “application/x-authoware-bin” },

{ “.aam”, “application/x-authoware-map” },

{ “.aas”, “application/x-authoware-seg” },

{ “.acx”, “application/internet-property-stream” },

{ “.ai”, “application/postscript” },

{ “.aif”, “audio/x-aiff” },

{ “.aifc”, “audio/x-aiff” },

{ “.aiff”, “audio/x-aiff” },

{ “.als”, “audio/X-Alpha5” },

{ “.amc”, “application/x-mpeg” },

{ “.ani”, “application/octet-stream” },

{ “.apk”, “application/vnd.android.package-archive” },

{ “.asc”, “text/plain” }

默认的消息转换器

基于性能的选择考虑,默认的RestTemplate无参构造方法不包含任何消息转换器。但是,如果调用了布尔版本的构造方法并传递true,则构造器会为一些主要的mime类型增加转换器。当然自己也可以编写自定义的转换器,并通过messageConverters属性添加。
RestTemplate生成的转换器包括:ByteArrayHttpMessageConverter, StringHttpMessageConverter, 和ResourceHttpMessageConverter。如果Android的版本在2.2或以上,那么XmlAwareFormHttpMessageConverter 和SourceHttpMessageConverter也会被添加。否则,若在2,.1版本,因为缺少对XML的必要支持,这两个转换器会被FormHttpMessageConverter 代替。我们来看看代码中是如何初始化的。

如果不设置,则使用该默认的消息转换器:

/**
* Identifies and initializes default {@link HttpMessageConverter} implementations.
*/

private static class DefaultMessageConverters {
private static final boolean javaxXmlTransformPresent =
ClassUtils.isPresent(“javax.xml.transform.Source”, RestTemplate.class.getClassLoader());

private static final boolean simpleXmlPresent =
ClassUtils.isPresent(“org.simpleframework.xml.Serializer”, RestTemplate.class.getClassLoader());

private static final boolean jackson2Present =
ClassUtils.isPresent(“com.fasterxml.jackson.databind.ObjectMapper”, RestTemplate.class.getClassLoader()) &&
ClassUtils.isPresent(“com.fasterxml.jackson.core.JsonGenerator”, RestTemplate.class.getClassLoader());

private static final boolean gsonPresent =
ClassUtils.isPresent(“com.google.gson.Gson”, RestTemplate.class.getClassLoader());

public static void init(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());

// if javax.xml.transform is not available, fall back to standard Form message converter
if (javaxXmlTransformPresent) {
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
else {
messageConverters.add(new FormHttpMessageConverter());
}
if (simpleXmlPresent) {
messageConverters.add(new SimpleXmlHttpMessageConverter());
}
if (jackson2Present) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}
}
ByteArrayHttpMessageConverter

该转换器可以从HTTP请求或应答中读写字节数组,默认情况下,该转换器支持所有的media types (*/*),同时写数据会以Content-Type为application/octet-stream(任意二进制数据)的形式。

FormHttpMessageConverter

能处理格式化的数据。支持用application/x-www-form-urlencoded(格式化编码数据)和multipart/form-data(多组件格式数据)的读写转换,格式化的数据通常用一个MultiValueMap<String, String>数据结构来读写。

XmlAwareFormHttpMessageConverter

是FormHttpMessageConverter的扩展,通过SourceHttpMessageConverter增加了对XML转换的支持。

ResourceHttpMessageConverter

能处理资源。支持对所有类型的读操作,写资源支持application/octet-stream。

SourceHttpMessageConverter

可以将请求或应答数据转换成javax.xml.transform.Source类型,只支持DOMSource, SAXSource, 和StreamSource三种类型。默认情况下支持text/xml和 application/xml两种XML可扩展标记语言MIME类型。

StringHttpMessageConverter

可以将请求或应答数据转换成String类型,支持所有的text media类型(text/*),支持Content-Type为text/plain(原文数据)写数据。

SimpleXmlHttpMessageConverter

支持对XML格式的读写处理,需要第三方包Simple XML serializer。XML的映射工作可以通过该包提供的注解方式进行配置。如果有需要提供附加的控制功能,可以通过切面的形式通过一个可定制的Serializer进行功能的加强。默认情况下,支持读写application/xml, text/xml, 和application/*+xml这几种MIME类型。

需要注意的是,此框架和Spring OXM并不兼容。他是用在SFA中的独立的第三方XML串行化实现。基于maven的依赖如下

<dependency>
<groupId>org.simpleframework</groupId>
<artifactId>simple-xml</artifactId>
<version>${simple-version}</version>
</dependency>

MappingJackson2HttpMessageConverter
基于Jackson框架提供对请求与应答数据的Json-Obejct转换。对于Json的映射配置可以Jackson提供的注解支持。当串行化/反串行化需要时也支持切面式的控制管理,默认支持application/json类型的读写。
请注意该转换器和GsonHttpMessageConverter 转换器都支持application/json类型的数据转换,因此*好只添加一款转换器,因为RestTemplate 会优先选择他*早匹配到的转换器进行数据转换,所以两款转换器都添加会导致不可预料的结果。

MappingJackson2HttpMessageConverter如果不是通过maven而是手工添加,还需要在lib包中引入jackson-annotations和jackson-core jars。

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>

也可以通过MappingJacksonHttpMessageConverter支持 Jackson JSON Processor包。同样,手工导入需要引入jackson-core-asl jar。

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson-version}</version>
</dependency>
GsonHttpMessageConverter
基本上和MappingJackson2HttpMessageConverter的功能一直,也支持注解和切面式。

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson-version}</version>
</dependency>
(以上也可以使用gradle插件进行配置)

使用gzip压缩

// 添加 Accept-Encoding 消息头属性,设置为gzip
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAcceptEncoding(ContentCodingType.GZIP);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// 注意调用的方法
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);

Gzip压缩能有效的减少通讯传输的信息量。Gzip格式传输必须在服务器端被支持。通过将内容的请求头Accept-Encoding设置为gzip,可以向服务器端请求使用gzip压缩传输。如果服务器端支持,将返回压缩后的数据。RestTemplate 将检查Content-Encoding属性判断应答返回数据是否是压缩过的,是则用GZIPInputStream 去解压缩。目前Content-Encoding仅支持gzip格式。
需要注意,如果在2.3或以后的版本使用基于J2SE的标准连接SimpleClientHttpRequestFactory,Android框架会自动在Accept-Encoding中设置gzip,如果希望是gzip压缩失效,必须在header信息中增加其他标志值:
HttpHeaders requestHeaders = new HttpHeaders();
//增加如下标识
requestHeaders.setAcceptEncoding(ContentCodingType.IDENTITY);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
通过GET方式获取JSON数据

首先需要定义一个与返回的JSON格式对应的POJO类。

public class Event {
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public String setTitle(String title) {
this.title = title;
}
}
使用Rest请求:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Event[] events = restTemplate.getForObject(url, Event[].class);

也可以通过设置请求头Accept信息的方式
// 显式设置 Accept header
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType(“application”,”json”)));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
ResponseEntity<Event[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Event[].class);
Event[] events = responseEntity.getBody();

其中MappingJackson2HttpMessageConverter可以换成GsonHttpMessageConverter,作为另一种实现JSON解析的选择。

 

通过GET方式获取 XML 数据

我们使用刚刚用过的Event对象来演示XML的使用方法,注意Event的类及属性都有注解。

@Root
public class Event {
@Element
private Long id;
@Element
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public String setTitle(String title) {
this.title = title;
}
}
如果需要解析一组Event的数据,我们还需要定义基于List的包装类。

@Root(name=”events”)
public class EventList {
@ElementList(inline=true)
private List<Event> events;
public List<Event> getEvents() {
return events;
}
public void setEvents(List<Event> events) {
this.events = events;
}
}
编写请求代码如下:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new SimpleXmlHttpMessageConverter());
EventList eventList = restTemplate.getForObject(url, EventList.class);
和刚才一样,也可以通过设置请求头Accept信息的方式:

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType(“application”,”xml”)));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new SimpleXmlHttpMessageConverter());
ResponseEntity<EventList> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, EventList.class);
EventList eventList = responseEntity.getBody();
通过POST的方式发送 JSON 数据

我们定义了如下的POJO用于演示:

public class Message {
private long id;
private String subject;
private String text;
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
接着是编辑数据和发送请求的代码

// 编辑数据
Message message = new Message();
message.setId(555);
message.setSubject(“test subject”);
message.setText(“test text”);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
String response = restTemplate.postForObject(url, message, String.class);
同样,通过设置请求头Content-Type信息也是可以的

// 编辑数据
Message message = new Message();
message.setId(555);
message.setSubject(“test subject”);
message.setText(“test text”);
// 设置 Content-Type
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(new MediaType(“application”,”json”));
HttpEntity<Message> requestEntity = new HttpEntity<Message>(message, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
String result = responseEntity.getBody();
(注意:如果想用spring for android的json解析功能,服务器需要返回的type类型应该为application/json而不是通常的text/html)

其他: Spring for Android还提供了三方授权,本身提供facebook和twtter的三方授,需要第三方包的支持。提供了Spring Social组件用于创建自己的连接工厂,并提供加密和保存信息的功能。

Spring for Android的核心部分比较强调服务器和客户端之间的数据交流,服务器需要确实返回指定的MIME类型,客户端才能进行更加简便的数据解析。

spring-android的使用

了解J2EE的人都会知道spring这个开源框架,不过哥对J2EE的开发没什么兴趣,太重量级了,不适合互联网的应用,还是喜欢php多点,不过sping在移动开发这块也推出了spring-android,spring-android可以做什么?有什么优势呢?

spring-android主要提供了两个重要的功能:

1.Rest模板,很多Android应用都要与服务器进行交互,而现在很多互联网应用的服务器端都会提供Rest服务,数据格式一般是json、xml、rss等,如果使用spring-android,这将大大方便你的Android应用与服务器端的交互,spring-android在解析json,xml都是非常方便的;

2.Auth授权验证,现在很多互联网应用都提供了开放的API服务,而你的Android应用要接入到这些服务中去,往往要经过授权才行,现在很多应用都使用Auth授权认证,如twitter、facebook、新浪微博等,如果使用spring-android,在授权验证这块将会非常方便。

如何使用spring-android呢?

1.首先要去http://www.springsource.org/spring-android 下载spring-android,然后解压。

2.新建一个Android项目,然后将解压后的spring-android里的spring-android-core-1.0.1.RELEASE.jar和spring-android-rest-template-1.0.1.RELEASE.jar放到Android项目的lib目录下,因为要访问在网络,所以要在AndroidManifest.xml文件下加入<uses-permission android:name=”android.permission.INTERNET”/>​

3.acitivity_main.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
< RelativeLayout 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"
     >
     < TextView
         android:id = "@+id/result_text"
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:layout_centerHorizontal = "true"
         android:layout_centerVertical = "true"
         tools:context = ".MainActivity" />
</ RelativeLayout >

4.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
package com.hxxy.springforandroidfirstdemo;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         final TextView resultTextView = (TextView) findViewById(R.id.result_text);
         AsyncTask<String, Void, String> simpleGetTask =  new AsyncTask<String, Void, String>() {
             @Override
             protected String doInBackground(String... params) {
                 // executed by a background thread
                 // 创建一个RestTemplate实例
                 RestTemplate restTemplate = new RestTemplate();
                 // 添加字符串消息转换器
                 restTemplate.getMessageConverters().add( new StringHttpMessageConverter());
                 return restTemplate.getForObject(params[ 0 ], String. class );
             }
             @Override
             protected void onPostExecute(String result) {
                 resultTextView.setText(result);
             }
         };
         String url = "http://www.chenwg.com" ;
         // 完成时更新resultTextView
         simpleGetTask.execute(url);
     }
}

 

android 实现类似个人中心的界面设计

From:android 实现类似个人中心的界面设计

上效果图:
这里写图片描述
这里写图片描述

先理清设计思路:
1、外层用linearlayout包裹,linearlayout采用shape,搭上描边、圆角和填充背景色。
2、里层采用relativelayout填充进textview、imageview。
思路搞清后,很简单就两步。
先上布局代码:

  1. <LinearLayout style=“@style/PersonalMainLayoutStyle” >
  2. <RelativeLayout style=“@style/FindBottomStyle” >
  3. <TextView
  4. style=“@style/PersonalTextStyle”
  5. android:text=“我的订单” />
  6. <ImageView
  7. android:id=“@+id/iv_drop_down”
  8. style=“@style/PersonalRightIconStyle”
  9. android:src=“@drawable/android_list_idex” />
  10. </RelativeLayout>
  11. </LinearLayout>

 

linearlayout布局属性代码:

  1. <style name=“PersonalMainLayoutStyle”>
  2. <item name=“android:layout_width”>match_parent</item>
  3. <item name=“android:layout_height”>wrap_content</item>
  4. <item name=“android:layout_margin”>10dp</item>
  5. <item name=“android:background”>@drawable/background_corners</item>
  6. <item name=“android:orientation”>vertical</item>
  7. <item name=“android:padding”>1dp</item>
  8. </style>

 

relativelayout布局属性代码:

  1. <style name=“FindBottomStyle”>
  2. <item name=“android:layout_width”>match_parent</item>
  3. <item name=“android:layout_height”>30dp</item>
  4. <item name=“android:layout_margin”>5dp</item>
  5. <item name=“android:background”>@drawable/more_activity_item_selector_bottom_corners</item>
  6. </style>

 

textview和imageview的属性代码可以自己设计了。

下面是drawable的设计代码.
看到上边relativelayout的item中引用了drawable-more_activity_item_selector_bottom_corners,个人感觉好像没什么卵用,主要是linearlayout的drawable,但是我没试,还是贴出来吧
relativelayout-drawable:

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <selector
  3. xmlns:android=“http://schemas.android.com/apk/res/android”>
  4. <item android:state_pressed=“true”>
  5. <shape>
  6. <solid android:color=“#ffffe381” />
  7. <stroke android:width=“0.0dip” android:color=“#ffcccccc” />
  8. <corners android:bottomLeftRadius=“6.0dip” android:bottomRightRadius=“6.0dip” />
  9. </shape>
  10. </item>
  11. <item>
  12. <shape>
  13. <solid android:color=“#ffffffff” />
  14. <stroke android:width=“0.0dip” android:color=“#ffcccccc” />
  15. <corners android:bottomLeftRadius=“6.0dip” android:bottomRightRadius=“6.0dip” />
  16. </shape>
  17. </item>
  18. </selector>

 

linearlayout-drawable:

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <selector xmlns:android=“http://schemas.android.com/apk/res/android”>
  3. <item><shape>
  4. <solid android:color=“#ffffffff” />
  5. <stroke android:width=“1.0dip” android:color=“#ffcccccc” />
  6. <corners android:radius=“6.0dip” />
  7. </shape></item>
  8. </selector>

 

Android 个人中心界面实现

效果展示

%title插图%num
依赖说明:
implementation ‘com.github.bumptech.glide:glide:3.7.0’

implementation ‘jp.wasabeef:glide-transformations:2.0.1’

implementation ‘de.hdodenhof:circleimageview:2.1.0’

glide框架:用于加载图片
glide-transformations:用于磨砂实现
circleimageview:圆形图片view
xml代码:
<?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”
android:orientation=”vertical”
xmlns:app=”http://schemas.android.com/apk/res-auto”
>
<android.support.v7.widget.Toolbar
android:layout_width=”match_parent”
android:layout_height=”?attr/actionBarSize”
android:background=”@color/colorPrimary”
android:id=”@+id/toolbar_fragment_personal”
app:title=”个人中心”

>
</android.support.v7.widget.Toolbar>
<!–磨砂头像–>
<RelativeLayout

android:layout_below=”@+id/toolbar_fragment_personal”
android:id=”@+id/relative”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<ImageView
android:id=”@+id/h_back”
android:layout_width=”match_parent”
android:layout_height=”180dp” />
<ImageView
android:id=”@+id/h_head”
android:layout_width=”80dp”
android:layout_height=”80dp”
android:layout_centerInParent=”true” />
<RelativeLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_alignBottom=”@id/h_back”
android:layout_marginBottom=”20dp”
android:orientation=”horizontal”>
<ImageView
android:id=”@+id/user_line”
android:layout_width=”1dp”
android:layout_height=”25dp”
android:layout_centerHorizontal=”true”
android:layout_marginLeft=”15dp”
android:background=”@android:color/white” />
<TextView
android:id=”@+id/user_name”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_toLeftOf=”@id/user_line”
android:text=”张三”
android:textColor=”@android:color/white”
android:textSize=”17sp” />
<TextView
android:id=”@+id/user_val”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginLeft=”15dp”
android:layout_toRightOf=”@id/user_line”
android:text=”182****5882″
android:textColor=”@android:color/white”
android:textSize=”17sp” />
</RelativeLayout>

</RelativeLayout>

<!–子项–>
<LinearLayout
android:id=”@+id/ll”
android:orientation=”vertical”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/username”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”用户名”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/changepw”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”修改密码”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/sex”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”性别”
android:textColor=”#000000″
android:textSize=”20dp”
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/version”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”版本”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>

</LinearLayout>

</LinearLayout>

在java文件中动态设置图片

//设置背景磨砂效果
Glide.with(this).load(R.drawable.touxiang)
.bitmapTransform(new BlurTransformation(getActivity(), 25), new CenterCrop(getActivity()))
.into(hBack);
//设置圆形图像
Glide.with(this).load(R.drawable.touxiang)
.bitmapTransform(new CropCircleTransformation(getActivity()))
.into(hHead);

Android Studio 开发 简易版音游APP

FREE  ——简易版音游APP
一、APP介绍
通过识别本地曲库,对音频文件进行识别提取出时间点,来产生滑块进行动态点击的畅玩过程,享受音乐的律动美感。界面主要仿照节奏大师等音游app,整体风格呈黑金色。(注:此app开发为课程作业,部分图片来自网图非原创,未曾商用)

 

二、APP特点
1.多种模式选择

常规设置:模式可选择双轨道或者四轨道;滑块数量可以选滑块较少或滑块较多;滑块速度可以选择慢速、中速、快速。

高级设置(开发人员选项):通过调整样本窗口大小、样本窗口数量、阈值权重等参数来调整滑块的数量。

2.支持本地所有MP3、WAV格式的音频

不同于曲库人为摆放滑块的位置,本app滑块位置是根据音频自动生成的,暂时配置有MP3、WAV格式音频文件的识别加载,能够较大程度地支持本地音频文件。

3.兼容不同分辨率的设备

通过android开发特有的dp单位适应不同分辨率手机的开发环境,通过ppi屏幕分辨率密度进行px与dp单位之间的转换,本APP能够适应ldpi、mdpi、hdpi、xhdpi、xxhdpi等不同分辨率密度,适配更多机型。

 

三、项目难点
1.滑块的滑动

通过比较Transaction、ObjectAnimator、ValueAnimator等动画效果,发现transaction只是表面移动,实际布局位置未改变,无法识别移动的位置,而且有的无法设置动画延迟时间,所以*后采用ObjectAnimatior为滑块滑动的主要部分。把滑块设为单独的EachButton、LandEachButton类,来动态设置它的动画起止位置,延迟时间等。这里动画延迟是结合音频节奏点的时间,延迟到那个节奏点的时候滑块动画才开始。

滑块的滑动主要用到的文件有Classes.EachButton、Classes.LandEachButton。

2.点击效果

这里点击时生成的best、good、miss字样主要是通过自定义Toast的setView方法实现,通过得到点击时该轨道列离得*近的滑块的位置与按钮位置之差d,得出best、good、miss指标的不同d的范围来得出点击评价,这里miss字样是在eachButton内部的onAnimationEnd()方法内部进行判别的。

点击效果主要用到的文件有toast.xml、toast_land.xml。

3.节奏点的识别

节奏点的识别主要用到音频采样、傅里叶变换(FFT)的知识,音频采样得到时域信号,这个信号可以看成是多个正弦波叠加的结果,通过傅里叶变换得到一段信号(一个样本窗口)里的关键频率,实现时域映射到频域,并与周围几个样本窗口的关键频率求均值加权得到阈值,大于阈值的信号点就可以看做节奏的起点,然后这个信号点的位置比例乘以总时间即为节奏点的时间,依此设置滑块延时。

实验过程中先是找到了一个wav格式文件画出波形图的样例,自己解读实现了一下,然后找到了mp3转化为wav格式的方法,然后放到android里面发现低于26的API不支持javax.sound等的包,于是又学习了mp3文件的格式,进行帧读取,后来发现大部分mp3文件是压缩过的,后来就仿照wav文件处理的代码自己实现了一个mp3文件的识别(有些数据流结构对不上mp3的标准帧格式,所以只是大致识别)。

节奏点识别主要用到的文件有WavHandle.WaveFileReader、Mp3Handle.Mp3FileReader、Classes.FFT 、Classes.HandleData。

4.兼容其他设备

由于activity中滑块位置的设置和获取是以px为单位的,所以需要转化为dp单位来兼容不同分辨率。通过得到设备的宽度getWindowManager().getDefaultDisplay().getWidth();对应不同的1dp=npx转换,其中对应关系(width,n)(240,0.75)(320,1.0)(480,1.5)(720,2.0)(1080,3.0)。

5.其他

申请读取内存的服务的实现。

通过MediaStore.Audio.Media.EXTERNAL_CONTENT_URI读取本地曲库,通过MediaStore.Audio.Media.DISPLAY_NAME等得到歌曲信息,通过RecycleView呈现歌单。其中用到的文件有Classes.Music、Classes.MusicAdapter。

通过广播、BaseActivity、ActivityCollector实现强行下线,避免重复打开活动。其中用到的文件有Classes.ActivityCollector、Classes.BaseActivity。

强制横屏的实现,通过layout_weight设置均分宽度居中。

 

四、APP界面
(注:此app开发为课程作业,图片来自网图非原创,未曾商用)

1.Logo

 

2.首页(MainActivity)

%title插图%num

3.歌单页面(MusicViewActivity)

%title插图%num

4.基础模式选择(OriginChoiceActivity)

%title插图%num

5.高级设置(ChoiceActivity)

%title插图%num

6.双轨道模式界面(GameActivity)

%title插图%num

7.四轨道模型界面(FourGameActivity)

%title插图%num

8.分数结果界面(ResultActivity)

%title插图%num

六、改进空间
(时间限制,部分功能未曾实现)

1.暂停功能

2.连击效果

3.歌曲搜索功能

4.排行榜

5.人工控制节奏点

AndroidStudio实用教程:多界面跳转

1.先创建一个项目,项目文件如下:

在这里插入图片描述

2.activity_double_window.xml代码如下


<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".DoubleWindow">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

 

3.新建一个布局文件

在这里插入图片描述

4.修改好名字以后Finish
在这里插入图片描述

5.新建一个java类文件

在这里插入图片描述

6.修改好名字以后OK

在这里插入图片描述

7.刚创建的java类源码

package com.shawna.doublewindow;

import android.app.Activity;
import android.os.Bundle;

public class two extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);
    }
}

 

8.在AndroidManifest.xml文件中增加类文件关联

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shawna.doublewindow">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".DoubleWindow">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        //这一句 ↓
        <activity android:name=".two"></activity>
    </application>

</manifest>

 

9.在一开始创建的主页面上放一个按钮,按钮按下事件名称函数为play

在这里插入图片描述

10.DoubleWindow代码如下:

package com.shawna.doublewindow;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class DoubleWindow extends AppCompatActivity {

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

    //按钮点击事件
    public void play(View view){
        //切换页面
        Intent intent = new Intent(DoubleWindow.this,two.class);
        startActivity(intent);
    }
}

 

 

Android Studio 点击按钮跳转新界面

问题描述
首先,我们有两个Java文件和与之绑定的xml文件。此处以HistoryActivity.java,activity_history.xml 和 EventDetail.java,activity_event_detail.xml为例。我们要实现在HistoryActivity界面中添加一个按钮,并且点击跳转到EventDetail界面。

%title插图%num
为HistoryActivity界面添加按钮
在其对应的activity_history.xml 中:

<?xml version=”1.0″ encoding=”utf-8″?>
<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”
tools:context=”.HistoryActivity”>

<Button
android:id=”@+id/History”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Historical Event”
android:layout_alignParentLeft=”true”
android:layout_alignParentStart=”true”/>
</android.support.constraint.ConstraintLayout>

我们通过android:id=”@+id/History”语句讲button的id设置为History,在之后设置点击事件时使用。

为History按钮添加点击事件
在HistoryActivity.java中:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.widget.Button;
import android.widget.*;

public class HistoryActivity extends AppCompatActivity {
Button button = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
button = (Button)findViewById(R.id.History);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(HistoryActivity.this,EventDetail.class);
startActivity(intent);
}
});
}

}

通过之前定义的button的id来找到对应button,为之设置点击监听。当发生点击事件时,通过Intent进行跳转。
#在manifests->AndroidManifest.xml中添加activity(这个步骤通常是添加点击事件之后系统自动生成,可以进行检查)

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.xff.tm”>

<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.HistoryActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<activity android:name=”.EventDetail”></activity>
</application>

</manifest>

EventDetail.java,activity_event_detail.xml
作为被跳转的界面,这两个文件只需要完成自己的功能即可:
EventDetail.java:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class EventDetail extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_event_detail);
}
}

activity_event_detail.xml:

<?xml version=”1.0″ encoding=”utf-8″?>
<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”
tools:context=”.EventDetail”>

</android.support.constraint.ConstraintLayout>

华为跨代研发3nm芯片,命名麒麟9010…

因为受到制裁的影响,去年的麒麟9000发布之后,华为就表示这将会是麒麟旗舰芯片的*唱。而且在去年9月15号之后,各大代工厂都无法替华为代工芯片,这也就导致去年下半年发布的MATE 40系列迄今为止都处于缺货状态。

%title插图%num
华为所面临的的困境并非只有芯片问题,在系统方面也受到了制约。好在软件和系统方面华为已经提前布局,鸿蒙已经开始适配手机,而且也有不少开发者加入到华为的阵营。但是说回到硬件问题,因为无论是华为自己还是国内厂商,都无法帮助华为度过这个难关。所以今年下半年的MATE 50系列是否可以顺利推出还是一个悬念。

虽然硬件无法制造,这并不意味着华为就此放弃了芯片的研发工作。根据*新爆料显示华为新一代的麒麟芯片还在研发当中,并且会直接使用3nm工艺,暂时命名为麒麟9010。

要知道研发芯片是一件很费钱的事情,之前华为推出麒麟芯片还可以卖出去回血,现在就只能拿来练手了,以保证自己的业务不生疏。所以华为研发出来的新一代芯片只能测试小量验证流片,并不能代工生产。

%title插图%num
今年新一代的工艺将会是5nm+,明年才会是3nm,所以华为应该是直接跳过了5nm+工艺,跨代研发了。要知道去年的麒麟9000提升非常大,性能上与骁龙888的差距微乎其微。现在华为并没有放弃研发工作,将来有一天解决了代工问题,相信依旧会处于手机芯片的一流行列。

此外,华为麒麟团队还发布了新年致辞。从2009年的K3V2,到2020年的麒麟9000,华为经历了十年风雨,十年征程。未来,也将会不忘初心,砥砺前行。相信唯坚持,得突破。

%title插图%num
除了华为自身没有放弃芯片的研发设计,国内的光刻机技术也在稳步的发展当中。虽然这个进度非常艰难,但是还是有些进展的。目前新型材料工艺也在紧罗密布的探讨研发中,根据消息透露,未来2-3年光刻机或者是碳基芯片领域必有一个会突破。

我们不清楚华为以及国内的困境还要持续多久,就像麒麟团队说的那样唯坚持,得突破。 也希望华为能够一直坚持下去。想获取更多科技资讯,快来关注我们吧!

iOS 开发过程中遇到的那些奇葩的坑

1.打印异常:dyld: lazy symbol binding failed: Symbol not found: _objc_unsafeClaimAutoreleasedReturnValue(

dyld_sim`dyld_fatal_error:

0x60e000 <+0>: int3

->  0x60e001 <+1>: nop


问题描述:本人开发了一个iOS项目,开发环境:电脑环境Mac OS 10.12.2,编译环境:Xcode8.2.1,测试环境:iOS 10.2.1、iOS 8.4以及iOS 9.3.5 。应用程序iOS 10 和iOS 9.3.5环境运行正常,但是在iOS 8.4运行不起来,直接闪退,如图所示:

%title插图%num

问题解决办法:

检查调用的静态库,查看静态库*低兼容版本(Department Target)是多少,如果是静态库*低兼容版本比你项目的兼容版本高,就需要修改之后重新打包静态库,我的问题就是 公司的SDK开发人员打包静态库的时候是用Xcode 7.2(默认*低兼容版本未iOS 9.2)打包的,忘记改*低兼容版本了,被坑了好几天,终于找到问题了

2.程序编译正常、运行正常但是打包时候报错,异常信息如下:

Undefined symbols for architecture armv7:

“_OBJC_CLASS_$_*****”, referenced from:

objc-class-ref in ********.o

ld: symbol(s) not found for architecture armv7

clang: error: linker command failed with exit code 1 (use -v to see invocation)

%title插图%num

如果你的项目也是运行正常,但是打包出问题了就可能是Build Active Architecture Only这个属性的问题,如下图所示:

%title插图%num

问题就出在这里,Debug模式下Build Active Architecture Only设置为YES,意思是只编译当前设备所对应的系统环境下,如果设置为NO,意思是编译为你所包含的所有设备的系统环境下通用的类型,这时候就出问题了!如果是为了发布而打包应用,这时候就不能把这个键值对设置为YES,这会导致你的应用上架后在其他设备不同系统上出现问题甚至无法运行的后果,其实出现问题的原因,上边的异常信息已经显示出来了,就是红色箭头指示的地方

%title插图%num

我这边的原因是公司封装SDK的人员在打包静态库的时候忘记修改这个键值对(默认的是Debug模式为YES,Release模式为NO),要把生成静态库的方法(运行或者编译)设置为NO,修改后重新替换即可