react-native工程unable to load script from assets index.android.bundle的解决办法

1. (1)在android\app\src\main下建立一个assets文件夹

(2)react-native bundle –platform android –dev false –entry-file index.js –bundle-output android/app/src/main/assets/index.android.bundle –assets-dest android/app/src/main/res
(3)再次执行react-native run-android
2. 如果执行上面时出现关于accessibilityInfo的错误,则删除工程,按照下面的顺序执行:
react-native init AwesomeProject
cd AwesomeProject
react-native run-android
npm uninstall react-native
npm install --save react-native@0.55.4
react-native run-android
npm install --save babel-core@latest babel-loader@latest
npm uninstall --save babel-preset-react-native
npm install --save babel-preset-react-native@4.0.0
react-native run-android
然后再重新执行第1步。

安卓系统推送的问题

安卓系统由于防火墙的原因,GCM在国内不能使用。目前国内的做法是在app里集成第三方提供的推送sdk。两者的原理基本相同,就是在手机上维持一个长连接到服务器,这样的话服务器就能主动向手机发消息,而不是被动的接收手机发送的请求后再向手机端返回响应。

但是由于国内手机厂商不同的定制rom,会杀死后台进程,上面提到的长连接就不能保持,从而导致推送消息不能送达手机。目前,有以下几种方法能解决或改善后台进程被杀的问题。

(1)用户量很大的app,比如微信,qq,它们会进入到手机厂商的白名单中。白名单中的app转到后台以后不会被杀死。如果你的app足够牛b有影响力,手机厂商可能会将其放入白名单。

(2)qq表面上转移到后台,但实际上并不是如此,它是在前台留有1个像素。(这条未经本人亲自验证)

(3)很多第三方推送sdk使用了共享连接的方式,即集成了该sdk的多个app使用同1个长连接接收推送消息。如果其中1个app被打开,则会建立1个长连接,其他的app也会使用这个长连接。所以,我们可以使用用户量高并且被很多知名的app使用的sdk,如果这些知名app被打开,那么我们的推送也会被送达。比如,据说王者荣耀就使用了腾讯公司的信鸽推送,我们可以考虑也使用这个sdk。

将String对象放入Integer型list引出的java类型擦出问题

在java里使用泛型声明1个Integer类型的list,一般情况下只能保存integer类型的对象。如果放入其它类型的对象则会报错。
其实,java的泛型只是伪泛型,即在编译前会做检查,编译后的字节码里不会保留泛型信息。

private static void foo() {
	List<String> list1 = new ArrayList<>();
	List<Integer> list2 = new ArrayList<>();
	System.out.println(list1.getClass());
	System.out.println(list1.getClass()==list2.getClass());
}

从上面的代码看到,2个不同类型的list的class都是java.util.ArrayList,不包含泛型信息。
由此想到,我们可以使用反射将一个不同类型的对象放入泛型集合里。

private static void addStringToIntList() {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	System.out.println(list);
	String value = "abc";
	try {
		list.getClass().getMethod("add", new Class[]{Object.class}).invoke(list, value);
	}
	catch(Exception e) {
		e.printStackTrace();
	}
	System.out.println(list);
}

ajax跨域问题的几种方法

在使用ajax的时候,浏览器会对调用的地址做限制。只有在相同协议、相同域名、相同端口的情况下才能调用成功。
例如,一个网页是http://www.abc.com/abc.html,在这个页面上,下面的ajax调用将不会成功:
(1)https://www.abc.com/*(*表示任意的字母或斜线)(由于使用了不同的协议)
(2)http://www.abcd.com/* 或 http://www1.abc.com/*(由于不是相同的域名。一级域名相同,二级或以下域名不相同,也会被认为是不同的域名)
(3)http://www.abc.com:8080/* (由于端口不同)

可以使用以下几种方法来解决ajax跨域问题。
我们使用a.test.com和b.test.com 2个域名。b.test.com下的html页面使用ajax方式调用a.test.com下的接口。
打开eclipse,新建1个dynamic web project,工程名为crossSite,当作a.test.com。新建1个tomcat server。
再建一个static web project,工程名为crossSite2,当作b.test.com。
使用nginx给2个域名做反向代理。在nginx里做如下配置:

	server {
		listen 			80;
		server_name 	a.test.com;
		location	/ {
						proxy_pass 		http://localhost:8080/crossSite/;
					}
	}

	server {
		listen 			80;
		server_name 	b.test.com;
		location		/ {
				root	crossSite2的存储目录\\WebContent\\;
				index	index.html;
		}
	}

修改本机的host文件,2个域名都指向127.0.0.1。
(1)jsonp
jsonp的原理基于以下:html页面里的script标签可以引用的地址不受限制。比如:http://b.test.com/index.html这个页面里可以包含script标签,它的src为”http://a.test.com/CrossSiteServlet”。
在crossSite工程里创建一个servlet,代码如下:


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;

/**
 * Servlet implementation class CrossSiteServlet
 */
@WebServlet("/CrossSiteServlet")
public class CrossSiteServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public CrossSiteServlet() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("utf-8");
		String callback = request.getParameter("callback");
		Map<String, String> map = new HashMap<String, String>();
		map.put("id", "id1");
		map.put("name", "name1");
		JSONObject jsonObj = new JSONObject(map);
		String returnStr = callback + "(" + jsonObj.toString() + ")";
		response.getWriter().println(returnStr);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

当我们访问http://a.test.com/CrossSiteServlet?callback=callback,将返回如下的数据:callback({“id”:”id1″,”name”:”name1″}),即一个js函数名以及json格式的数据。

在crossSite2工程webContent目录下新建一个html页面:index.html。内容如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-1.8.0.min.js"></script>
<script type="text/javascript">
    $().ready(function () {
        $.ajax({
            type: "get",
            async: true,
            url: "http://a.test.com/CrossSiteServlet",
            data:{},
            dataType: "jsonp",
            jsonp: "callback", //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
            success: function (json) {
                alert(json.id+" "+json.name);
            },
            error: function () {
                alert('fail');
            }
        });
    });
</script>
</head>
<body>
jsonp test
</body>
</html>

注意上面的jsonp后面的值为callback,这个值必须同上面servlet返回的js函数名一致。

(2)iframe
原理:在页面里嵌套一个iframe,这个iframe的src属性指向不同的域名,从而可以从这个iframe里获取到其它域名下的数据。
iframe也有跨域限制,正常情况下,b.test.com域名下的网页是不能访问它内部指向a.test.com域名的iframe里的内容的。
可以通过修改页面和iframe的document.domain属性,让它们都为一级域名test.com,这样就可以跨域访问了。
首先在crossSite2工程WebContent目录下新建index2.html,内容如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-1.8.0.min.js"></script>
<script type="text/javascript">
	document.domain='test.com';
	function getData(){
		document.getElementById('ifrm1').src="http://a.test.com/IframeCrossSiteServlet";
		//setTimeout(function(){alert($("#ifrm1").contents().find("div1").text())}, 3000);
		setTimeout(function(){alert($(document.getElementById('ifrm1').contentWindow.document.body).find('#div1').text())}, 3000);
	}
</script>
</head>
<body>
<iframe id="ifrm1" name="ifrm1" style="display: none;"></iframe>

<button type="button" onclick="getData()">get data</button>
</body>
</html>

注意上面将document.domain修改为‘test.com’,如果去掉这句就会报错。

然后在crossSite工程里新建一个servlet:IframeCrossSiteServlet。代码如下:

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class IframeCrossSiteServlet
 */
@WebServlet("/IframeCrossSiteServlet")
public class IframeCrossSiteServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public IframeCrossSiteServlet() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().append("<script type=\"text/javascript\">\r\n" +
				"document.domain='test.com';\r\n</script>

<DIV id=\"div1\">I am an iframe.</DIV>

");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

注意上面在doGet方法里,同样地将document.domain的值改为‘test.com’。

以上2种方法存在的问题:
1. jsonp只能使用get调用。即使在代码里改成post调用,实际上还是使用get调用。所以不够安全。
2. iframe只能在一级域名相同的情况下使用。如果一级域名不同,就不能使用了。

设计模式-状态模式

设计模式分3大类:创建型、结构性和行为型。状态模式是行为型模式的其中一种。它适用的场景是:一个对象的行为是由它的某个状态决定的,当状态改变时,行为会随之改变。
举一个例子:一个电扇,它的转动就是一个行为。这个行为由它所处的档位决定:1档是低速转动,2档是中速转动,3档是高速转动。
我们新建一个类Fan表示电扇,这个类有一个方法rotate()表示转动这个动作。如果不使用设计模式,我们的代码可能是这样:


class Fan {

	/**
	 * 
	 * @param level 表示档位
	 */
	public void rotate(int level){
		if(level==1){
			System.out.println("rotate at low speed");
		}
		else if(level==2){
			System.out.println("rotate at intermediate speed");
		}
		else if(level==3){
			System.out.println("rotate at high speed");
		}
	}
}

假如电扇新增加1档,必须要修改rotate方法,这样就违反了开闭原则。这时候,我们就可以使用状态模式。状态模式的意思是:我们将对象的某个动作委托给某个具体的状态。通常,我们有一个状态的接口,这个接口下有多个具体的实现类。在上面的例子里,档位(下面的代码里用Level表示)就是一个接口,1档,2档,3档是具体的实现类。下面是使用状态模式后的代码:


interface Level{
	public void rotate();
}

class Level1 implements Level {

	public void rotate() {
		System.out.println("rotate at low speed");
	}
	
}

class Level2 implements Level {

	public void rotate() {
		System.out.println("rotate at intermediate speed");
	}
	
}

class Level3 implements Level {

	public void rotate() {
		System.out.println("rotate at high speed");
	}
	
}

class Fan {

	private Level level;
	
	public Fan(Level level){
		this.level = level;
	}

	public Level getLevel() {
		return level;
	}

	public void setLevel(Level level) {
		this.level = level;
	}
	
	public void rotate(){
		level.rotate();
	}
	
}


public class StatePatternDemo {

	public static void main(String[] args) {
		Level level = new Level1();
		Fan fan = new Fan(level);
		fan.rotate();
		
		fan.setLevel(new Level2());
		fan.rotate();
		
		fan.setLevel(new Level3());
		fan.rotate();
	}

}


输出如下:
rotate at low speed
rotate at intermediate speed
rotate at high speed

从上面的代码可以看出:我们将电扇的rotate行为委托给具体的level对象。当新增加一个档位时,我们只需要新增一个具体的Level类就可以了。这样也符合了开闭原则。