I have an arduino nano with a HC-05 bluetooth module.I need to receive some data from that module into Unity so i made an android librarie(in Android Studio) that can handle the bluetooth connection.
The problem is that.For the first 2 seconds i receive data then the process seems to slow down.I wait up to 10 seconds between the data sets.
I read something about the standard bound rate of the module and i think that could be the source of the problem but when i change it from AT commands mode.Nothing seems to change.
*When i read the data from an Bluetooth terminal(from Google Play).It feels mutch faster than my app.
Some ideas please?
The library code
public class BluetoothConnector {
private static final BluetoothConnector ourInstance = new BluetoothConnector();
public static BluetoothConnector getInstance() {
return ourInstance;
}
ArrayList<BluetoothDevice> devices_true = new ArrayList<>();
ArrayList<String>device_names = new ArrayList<>();
final UUID mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothAdapter adapter;
BluetoothSocket skt;
BluetoothDevice cur_device;
BluetoothDevice con_device;
Set<BluetoothDevice> devices;
InputStream inputStream;
OutputStream outputStream;
private BluetoothConnector() {}
public String Loooper_Connect(){
try{
adapter = BluetoothAdapter.getDefaultAdapter();
devices = adapter.getBondedDevices();
for(BluetoothDevice device:devices)
{
devices_true.add(device);
device_names.add(device.getName());
}
cur_device = devices_true.get(device_names.indexOf("LooperController"));
if(cur_device == null)return "No controller found";
con_device = adapter.getRemoteDevice(cur_device.getAddress());
skt = con_device.createInsecureRfcommSocketToServiceRecord(mUUID);
adapter.cancelDiscovery();
skt.connect();
inputStream = skt.getInputStream();
outputStream = skt.getOutputStream();
outputStream.write("Device Connected".getBytes());
outputStream.flush();
return "Controller Connected!";
}
catch (Exception ex)
{
return "Error : " + ex.toString();
}
}
public String ReadData(){
byte[] primary_data = new byte[256];
try {
if(inputStream.available() > 0)
inputStream.read(primary_data);
else return "";
return new String(primary_data);
} catch (IOException e) {
return e.toString();
}
}
}
And my unity code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Main : MonoBehaviour {
// Use this for initialization
AndroidJavaClass bluetoothComunication;
AndroidJavaObject bluetoothCom;
public GameObject cub;
public string sol;
string d;
void Start () {
try
{
bluetoothComunication = new AndroidJavaClass("com.example.bluetoothlooper.BluetoothConnector");
bluetoothCom = bluetoothComunication.CallStatic<AndroidJavaObject>("getInstance");
sol = bluetoothCom.Call<string>("Loooper_Connect");
}
catch (Exception ex)
{
sol = ex.ToString();
}
}
string data;
void Update () {
try
{
data = bluetoothCom.Call<string>("ReadData");
if (data.Length > 1)
{
cub.SetActive(true);
}
else
{
cub.SetActive(false);
}
}
catch(Exception ex)
{
data = ex.ToString();
}
}
}
I figured out what happend and i will explain this in case that somebody has the same problem.The inputStream from java doesn't recognize line endings, so when you read the current buffer, you take everything from there.
For example if you write every second "Hello",when you will read this the result after 2 seconds will be "HelloHello" and that creates something verry strange in the main program(unity).
SO...to resolve this problem you need to implement an BufferedReader that will read line by line the received data.
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String d = reader.readLine();
return d;
I hope i figured out correctly what happens there.If i am wrong please correct me!
Related
I am an android developer, I develop apps for clients.
However, in client devices, some app crash issue usually happened. But it didn't happen in my device.
But I cannot reach my client, so, here is question:~~~~
Is there any plugin or tools that can implement into my android apps, so when the apps is crashed in my client's device, the crash logs will be sent to my email or somewhere I can check it, so I can debug it.
Please advise me, thanks
Go to https://play.google.com/apps/publish/ and publish your app. When users install your app and it crashes it notifies Google, and when you'll login to this control-panel you'll be able to find such reports under the "Reports" tab and then under "CRASHES & ANRS".
Try taking a look at Crashlytics: http://try.crashlytics.com/sdk-android/
I think you should extend Application class, and redefine Exception Handle with stable code (independed of android version).
Code example:
YourApplication.java
public class YourApplication extends Application {
#Override
public void onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler.inContext(getApplicationContext()));
super.onCreate();
}}
ExceptionHandler.java
final public class ExceptionHandler implements Thread.UncaughtExceptionHandler{
private final DateFormat formatter = new SimpleDateFormat("dd.MM.yy HH:mm");
private final DateFormat fileFormatter = new SimpleDateFormat("dd-MM-yy");
private String versionName = "0";
private int versionCode = 0;
private final String stacktraceDir;
private final Thread.UncaughtExceptionHandler previousHandler;
private ExceptionHandler(Context context, boolean chained) {
PackageManager mPackManager = context.getPackageManager();
PackageInfo mPackInfo;
try {
mPackInfo = mPackManager.getPackageInfo(context.getPackageName(), 0);
versionName = mPackInfo.versionName;
versionCode = mPackInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// ignore
}
if(chained)
previousHandler = Thread.getDefaultUncaughtExceptionHandler();
else
previousHandler = null;
stacktraceDir = String.format("/Android/data/%s/files/", context.getPackageName());
}
static ExceptionHandler inContext(Context context) {
return new ExceptionHandler(context, true);
}
static ExceptionHandler reportOnlyHandler(Context context) {
return new ExceptionHandler(context, false);
}
#Override
public void uncaughtException(Thread thread, Throwable exception) {
final String state = Environment.getExternalStorageState();
final Date dumpDate = new Date(System.currentTimeMillis());
if (Environment.MEDIA_MOUNTED.equals(state)) {
StringBuilder reportBuilder = new StringBuilder();
reportBuilder
.append("\n\n\n")
.append(formatter.format(dumpDate)).append("\n")
.append(String.format("Version: %s (%d)\n", versionName, versionCode))
.append(thread.toString()).append("\n");
processThrowable(exception, reportBuilder);
File sd = Environment.getExternalStorageDirectory();
File stacktrace = new File(
sd.getPath() + stacktraceDir,
String.format(
"stacktrace-%s.txt",
fileFormatter.format(dumpDate)));
File dumpdir = stacktrace.getParentFile();
boolean dirReady = dumpdir.isDirectory() || dumpdir.mkdirs();
if (dirReady) {
FileWriter writer = null;
try {
writer = new FileWriter(stacktrace, true);
writer.write(reportBuilder.toString());
} catch (IOException e) {
// ignore
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
// ignore
}
}
}
}
if(previousHandler != null)
previousHandler.uncaughtException(thread, exception);
}
private void processThrowable(Throwable exception, StringBuilder builder) {
if(exception == null)
return;
StackTraceElement[] stackTraceElements = exception.getStackTrace();
builder
.append("Exception: ").append(exception.getClass().getName()).append("\n")
.append("Message: ").append(exception.getMessage()).append("\nStacktrace:\n");
for(StackTraceElement element : stackTraceElements) {
builder.append("\t").append(element.toString()).append("\n");
}
processThrowable(exception.getCause(), builder);
}}
If your app will crased, you can find a log with StackTrace at /sdcard/android/data/you.package.name/files/ all log files.
Also you can check new log files at this folder at every app start. If you find new files, you can send this to your email.
Actually ,I had searched some questions and go to the github. But I'm new ,I cannot understand the example.
I want to create the http server in android so I can access it in PC browser.
I had instance a class extend nanohttpd, but the server just don't work. I don't know why ,my computer and phone are in the same WIFI,uh.....
public class MyHTTPD extends NanoHTTPD {
/**
* Constructs an HTTP server on given port.
*/
public MyHTTPD()throws IOException {
super(8080);
}
#Override
public Response serve( String uri, Method method,
Map<String, String> header, Map<String, String> parms,
Map<String, String> files )
{
System.out.println( method + " '222" + uri + "' " );
String msg = "<html><body><h1>Hello server</h1>\n";
if ( parms.get("username") == null )
msg +=
"<form action='?' method='get'>\n" +
" <p>Your name: <input type='text' name='username'></p>\n" +
"</form>\n";
else
msg += "<p>Hello, " + parms.get("username") + "!</p>";
msg += "</body></html>\n";
return new NanoHTTPD.Response(msg );
}
public static void main( String[] args )
{
try
{
new MyHTTPD();
}
catch( IOException ioe )
{
System.err.println( "Couldn't start server:\n" + ioe );
System.exit( -1 );
}
System.out.println( "Listening on port 8080. Hit Enter to stop.\n" );
try { System.in.read(); } catch( Throwable t ) {
System.out.println("read error");
};
}
}
Your sample code is missing one small detail - you create the server but you never call the "start()" method which kicks it off to listen for incoming connections. In your main() method, you could write
(new MyHTTPD()).start();
and all would be well, your server would respond the way you hoped it would.
The reason it works that way is twofold: I want the constructor to be a cheap, inexpensive operation, without side-effects. For instance, while unit testing, I call "start()" in the setup and "stop()" in the teardown methods of my jUnit test.
This is the code working for me, but I have different version of NANOHTTPD, I don't have time right now to test out your solution. Here is UploadServer class and Nano class. I return file-upload.htm from sdcard/Discover Control/Web path
public class UploadServer extends NanoHTTPD {
public UploadServer() throws IOException {
super(8080, new File("."));
}
public Response serve( String uri, String method, Properties header, Properties parms, Properties files ) {
File rootsd = Environment.getExternalStorageDirectory();
File path = new File(rootsd.getAbsolutePath() + "/Discover Control/Web");
Response r = super.serveFile("/file-upload.htm", header, path, true);
return r;
}
}
NanoHttpd class
NanoHTTPD.java
FILE UPLOAD
file-upload.htm
Hope this helps and enjoy your work.
Android Activities have a lifecycle and do not use a main() function.
If you want to start and stop the webserver as part of the Activity then you need call start and stop
in onPause and onResume, ie
public class MyActivity extends Activity {
private MyHTTPD mServer;
#Override
protected void onResume() {
super.onResume();
try {
mServer = new MyHTTPD();
mServer.start();
} catch (IOException e) {
e.printStackTrace();
mServer = null;
}
#Override
protected void onPause() {
super.onPause();
if(mServer != null) {
mServer.stop();
mServer = null;
}
}
}
An alternative is to implement the webserver as part of a Service.
In an app I'm working I have a requirement to keep the webserver running even if the user leaves the app. The only way to do this is to start and stop the webserver as part of a long-running Service that is not bound to the Activity. See Vogella's great tutorial on Android Services.
This code working for fine viewing html pages with css class which are in my assesst folders
androidWebServer.start();
this will start the server below code for server functions
public class AndroidWebServer extends NanoHTTPD {
Realm realm;
Map<String, String> parms;
DBHelper db = new DBHelper(OpenRAP.getContext());
boolean isStartedHS = MainActivity.isStartedHS;
private AsyncHttpServer server = new AsyncHttpServer();
private AsyncServer mAsyncServer = new AsyncServer();
private String TAG = "androidwebserver";
Storage storage = new Storage(OpenRAP.getContext());
public AndroidWebServer(int port) {
super(port);
}
public AndroidWebServer(String hostname, int port) {
super(hostname, port);
}
#Override
public String getHostname() {
return super.getHostname();
}
#Override
public Response serve(IHTTPSession session) {
Method method = session.getMethod();
String uri = session.getUri();
Map<String, String> files = new HashMap<>();
SharedPreferences prefs = OpenRAP.getContext().getSharedPreferences(MainActivity.mypreference, MODE_PRIVATE);
OpenRAP app = (OpenRAP) OpenRAP.getContext();
Storage storage = new Storage(OpenRAP.getContext());
String currentpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/www/";
String temp = Environment.getExternalStorageDirectory().getAbsolutePath() + "/www/temp/";
String ecarpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/www/ecars_files/";
String xcontent = Environment.getExternalStorageDirectory().getAbsolutePath() + "/www/xcontent/";
String Endpoint = session.getUri();
if (Endpoint.equals("/")) {
String answer = "";
try {
// Open file from SD Card
File root = Environment.getExternalStorageDirectory().getAbsoluteFile();
FileReader index = new FileReader(root +
"/www/openrap/index.html");
BufferedReader reader = new BufferedReader(index);
String line = "";
while ((line = reader.readLine()) != null) {
answer += line;
}
reader.close();
} catch (IOException ioe) {
Log.w("Httpd", ioe.toString());
}
return newFixedLengthResponse(answer);
}
Goal
Collect periodic updates of the LogCat and save (append) those chunks of text to a file on the SDcard
Problem
The Log class doesn't provide updates since a specific time-stamp
Possible solution
My plan is to periodically run code that is similar to: http://www.helloandroid.com/tutorials/reading-logs-programatically
or https://stackoverflow.com/a/9039352/550471
However, with one notable difference: use the -v time parameter to ensure that each line is time-stamped.
After each time the LogCat data is collected, the app will store the time-stamp of the last Log entry. The next time the LogCat data is collected the app will search through the text to find the time-stamp and then save the chunk of data to sdcard that was added to the Log since the specified time-stamp.
Possible problem
If the LogCat data is collected at too short periods then the CPU is busy processing a lot of 'old' data.
If the Logcat data is collected at too long periods then some data could be missed.
Is there a better way ?
This is what I came up with - it works very well when it doesn't freeze up.
As you might know, Runtime.getRuntime().exec("") has a pretty good chance of causing an ANR in Android earlier than Jelly Bean. If someone has a solution to overcome the ANR, then please share.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import android.os.Environment;
import android.util.Log;
/*
* For (compressed) buffer sizes, see: http://elinux.org/Android_Logging_System
* buffer:main = 64KB
* buffer:radio = 64KB
* buffer:system = 64KB
* buffer:event = 256KB
*
* NOTE: the 'command' must include "-d -v time" !!
* to switch buffers, use "-b <buffer>"
*/
public class LogCatReader {
// constants
private static final String CR = "\r\n";
private static final String END_OF_DATE_TIME = "): ";
private static final int DEFAULT_SEARCH_START_INDEX = 0;
// member variables
private StringBuilder mLog;
private LogThread mLogThread = null;
private String mLastLogReadToken = "";
private String mLogCommand = "";
private int mStringCapacity;
private File mFileTarget = null;
// constructor
public LogCatReader(String command, int capacity) {
mLogCommand = command;
mStringCapacity = capacity;
}
// returns complete logcat buffer
// note: takes about 1.5sec to finish
synchronized public StringBuilder getLogComplete() {
try {
// capacity should be about 25% bigger than buffer size since the
// buffer is compressed
mLog = new StringBuilder(mStringCapacity);
// command to capture log
Process process = Runtime.getRuntime().exec(mLogCommand);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
// append() is costly if capacity needs to be increased, be sure
// to reserve enough in the first place
mLog.append(line + CR);
}
} catch (IOException e) {
}
return mLog;
}
public String getLogUpdatesOnly() {
String strReturn = "";
StringBuilder sbLog = getLogComplete();
try {
int iStartindex = DEFAULT_SEARCH_START_INDEX;
// if there exists a token from a previous search then use that
if (mLastLogReadToken.length() > 0) {
iStartindex = sbLog.indexOf(mLastLogReadToken);
// if string not found then start at beginning
if (iStartindex == -1) {
// start search at beginning of log
iStartindex = DEFAULT_SEARCH_START_INDEX;
}
}
int iEndindex = sbLog.length();
// if token is found then move index to the next line
if (iStartindex > DEFAULT_SEARCH_START_INDEX) {
iStartindex = sbLog.indexOf(CR, iStartindex);
if (iStartindex != -1) {
iStartindex += CR.length();
} else {
// return an empty string
iStartindex = iEndindex;
}
}
// grab the data between the start and end indices
strReturn = sbLog.substring(iStartindex, iEndindex);
// grab date/time token for next search
iStartindex = sbLog.lastIndexOf(END_OF_DATE_TIME);
if (iStartindex != -1) {
iEndindex = iStartindex;
iStartindex = sbLog.lastIndexOf(CR, iEndindex);
iStartindex += CR.length();
if (iStartindex == -1) {
// read from beginning
iStartindex = 0;
}
mLastLogReadToken = sbLog.substring(iStartindex, iEndindex);
}
} catch (Exception e) {
strReturn = "";
}
return strReturn;
}
public void startPeriodicLogCatReader(int timePeriod, String logfilename) {
if (mLogThread == null) {
mLogThread = new LogThread(timePeriod, logfilename);
mLogThread.start();
}
}
public void stopPeriodicLogCatReader() {
if (mLogThread != null) {
mLogThread.interrupt();
mLogThread = null;
}
}
private class LogThread extends Thread {
private boolean mInterrupted;
private int mTimePeriod;// in seconds
private String mLogref;
private BufferedWriter mBuffWriter = null;
public boolean mPauseLogCollection = false;
// constructor: logfilename is optional - pass null to not use
public LogThread(int timePeriod, String logfilename) {
mTimePeriod = timePeriod;
if (logfilename != null) {
File fLogFolder = new File(
Environment.getExternalStorageDirectory() + "/logfiles");
if (fLogFolder.exists() == false) {
if (fLogFolder.mkdirs() == false) {
Log.e("LogCatReader",
"Could not create "
+ fLogFolder.getAbsolutePath());
}
}
mFileTarget = new File(
Environment.getExternalStorageDirectory() + "/logfiles",
logfilename);
if (mFileTarget.exists() == false) {
try {
// file doesn't yet exist - create a fresh one !
mFileTarget.createNewFile();
} catch (IOException e) {
e.printStackTrace();
mFileTarget = null;
}
}
}
}
#Override
public void interrupt() {
mInterrupted = true;
super.interrupt();
}
#Override
public void run() {
super.run();
// initialization
mInterrupted = false;
// set up storage
if (mFileTarget != null) {
try {
mBuffWriter = new BufferedWriter(new FileWriter(
mFileTarget, true), 10240);
} catch (IOException e) {
e.printStackTrace();
}
}
while ((mInterrupted == false) && (mBuffWriter != null)) {
if (mPauseLogCollection == false) {
// read log updates
mLogref = getLogUpdatesOnly();
// save log updates to file
try {
mBuffWriter.append(mLogref);
mBuffWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
if (!mInterrupted) {
try {
sleep(mTimePeriod * 1000);
} catch (InterruptedException e) {
}
}
}
if (mBuffWriter != null) {
try {
mBuffWriter.close();
mBuffWriter = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}// end of inner class
}// end of outer class
The procedure I used to find only the updates is to capture the date and time of the very last line of a logcat and use that as the search token next time around.
To use this class, the following is an example:
LogCatReader logcatPeriodicReader = new LogCatReader("logcat -b main -d -v time", 80 * 1024);//collect "main" buffer, exit after reading logcat
logcatPeriodicReader.startPeriodicLogReader(90, "log.txt");//read logcat every 90 secs
I have a android application with lots of buttons. If a button is pressed it sends a short cmd to a server via a socket.
Currently, when a button is pressed this adds a cmd to a list.
I have a worker thread that constantly checks the list for cmds and if it finds one opens a socket and sends the cmd.
This is not very efficient as the worker thread is constantly running. What would be the best way to improve this?
public class Arduino implements Runnable{
private static PrintWriter arduinoOutput;
private static Socket ss;
private static Queue<String> cmdsToSend=new LinkedList<String>();
private static String cmd;
public void run(){
while(true){
if(!cmdsToSend.isEmpty()){
cmd = cmdsToSend.poll();
System.out.println("send:"+cmd);
if(connect()){
arduinoOutput.println(cmd);
disconnect();
}
}
}
}
public static void sendCmd(String newcmd){
cmdsToSend.add(newcmd);
}
private static boolean connect(){
try {
ss = new Socket();
InetAddress addr = InetAddress.getByName("192.168.1.8");
int port = 23;
SocketAddress sockaddr = new InetSocketAddress(addr, port);
ss.connect(sockaddr, 2000);
arduinoOutput = new PrintWriter(ss.getOutputStream(),true); //Autoflush
return true;
} catch (UnknownHostException e) {
return false;
} catch (IOException e) {
return false;
}
}
private static void disconnect(){
arduinoOutput.close();
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The UI activity adds a cmd by calling Arduino.sendCmd("cmdName"); The cmds need to be sent as quickly as possible so a sleep in the loop is no good.
Any ideas or examples would be appreciated.
Use a wait/notify pattern. Put the sender on a thread with the list. Whenever there is something to write to the worker thread, have the writer add the command, and then notify the thread. If the thread is already awake, the notify will do nothing.
Here is a quick example, clearly the mechanism you will use to start the writing thread will be different.
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ThreadFactory;
public class Notifier
{
public static void main( String args[] )
{
Writer writingThread = new Writer();
writingThread.addToQueue( "Command 0" );
ThreadFactory.submitInSingleThread( writingThread );
for (int i = 1; i < 1000; i++)
{
writingThread.addToQueue( "Command " + i );
writingThread.notify();
}
}
static class Writer implements Runnable
{
private static Queue<String> cmdsToSend = new LinkedList<String>();
public void addToQueue( String cmd )
{
cmdsToSend.add( cmd );
}
#Override
public void run()
{
while( true )
{
if( !cmdsToSend.isEmpty() )
{
String cmd = cmdsToSend.poll();
System.out.println( "send:" + cmd );
if( connect() )
{
arduinoOutput.println( cmd );
disconnect();
}
}
synchronized( this )
{
wait(); //Can add a timer (100ms, for example)
}
}
}
}
}
Due to simplicity i have a text file with entries separated by ; and parses every line into an object. The problem is that the text file contains almost 10 000 rows.
I also need to create keys for each object im parsing so i can filter the results in a search interface.
It takes almost 16 seconds in emulator to parse the text and add the keys. I'm i doing something wrong here? Or is there a more efficient way?
Here is my database singleton:
public class Database {
private static Database instance = null; private final Map<String, List<Stop>> mDict = new ConcurrentHashMap<String, List<Stop>>();
public static Database getInstance() { if (instance == null) { instance = new Database(); } return instance; } public List<Stop> getMatches(String query) {
List<Stop> list = mDict.get(query);
return list == null ? Collections.EMPTY_LIST : list;
}
private boolean mLoaded = false;
/**
* Loads the words and definitions if they haven't been loaded already.
*
* #param resources Used to load the file containing the words and definitions.
*/
public synchronized void ensureLoaded(final Resources resources) {
if (mLoaded) return;
new Thread(new Runnable() {
public void run() {
try {
loadStops(resources);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private synchronized void loadStops(Resources resources) throws IOException
{
if (mLoaded) return;
Log.d("database", "loading stops");
InputStream inputStream = resources.openRawResource(R.raw.allstops);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while((line = reader.readLine()) != null) {
String[] strings = TextUtils.split(line, ";");
addStop(strings[0], strings[1], strings[2]);
}
} finally {
reader.close();
}
Log.d("database", "loading stops completed");
mLoaded = true;
}
private void addStop(String name, String district, String id) {
Stop stop = new Stop(id, name, district);
int len = name.length();
for (int i = 0; i < len; i++) {
String prefix = name.substring(0, len - i).toLowerCase();
addMatch(prefix, stop);
}
}
private void addMatch(String query, Stop stop) {
List<Stop> matches = mDict.get(query);
if (matches == null) {
matches = new ArrayList<Stop>();
mDict.put(query, matches);
}
matches.add(stop);
}
}
Here is some sample data:
Mosseporten Senter;Norge;9021014089003000;59.445422;10.701055;273
Oslo Bussterminal;Norge;9021014089004000;59.911369;10.759665;273
Långegärde;Strömstad;9021014026420000;58.891462;11.007767;68
Västra bryggan;Strömstad;9021014026421000;58.893080;11.009997;7
Vettnet;Strömstad;9021014026422000;58.903184;11.020739;7
Ekenäs;Strömstad;9021014026410000;58.893610;11.048821;7
Kilesand;Strömstad;9021014026411000;58.878472;11.052983;7
Ramsö;Strömstad;9021014026430000;58.831531;11.067402;7
Sarpsborg;Norge;9021014089002000;59.280937;11.111763;273
Styrsö;Strömstad;9021014026180000;58.908110;11.115818;7
Capri/Källviken;Strömstad;9021014026440000;58.965200;11.124384;63
Lindholmens vändplan;Strömstad;9021014026156000;58.890212;11.128393;64
Öddö;Strömstad;9021014026190000;58.923490;11.130767;7
Källviksdalen;Strömstad;9021014026439000;58.962414;11.131962;64
Husevägen;Strömstad;9021014026505000;58.960094;11.133535;274
Caprivägen;Strömstad;9021014026284000;58.958404;11.134281;64
Stensviks korsväg;Strömstad;9021014026341000;59.001499;11.137203;63
Kungbäck;Strömstad;9021014026340000;59.006056;11.140313;63
Kase;Strömstad;9021014026173000;58.957649;11.141904;274
You should add the information into a SQLite database and ship the app with the database in res/raw.
Additionally, the db file can often be effectively compressed into a zip file.
See this for more information: Ship an application with a database
The fastest way to load that data into memory is to place it right into .java file. E.g. stopNames={"Stop1", "Stop2", ...}; latitudes={...};
I do this in my public transit app, loading similar amounts of the data this way takes under a second, filtering is instant.